Refactor: standardize i18n context + add codemod to unify imports and hooks #20447

Open
opened 2026-02-21 20:07:29 -05:00 by yindo · 3 comments
Owner

Originally created by @morningstarxcdcode on GitHub (Nov 20, 2025).

Originally assigned to: @morningstarxcdcode on GitHub.

Self Checks

  • I have read the Contributing Guide and Language Policy.
  • This is only for refactoring, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report, otherwise it will be closed.
  • 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
  • Please do not modify this template :) and fill in all the required fields.

Description

  • Several files import i18n context with mixed names and patterns (I18N, I18n, useI18N, useI18n, useContext(I18n)). This inconsistency causes fragile imports, runtime errors, and adds maintenance burden.
  • Proposed refactor:
    1. Add compatibility aliases in web/context/i18n.ts:
      • export const I18NContext = createContext(...)
      • export const I18n = I18NContext (alias)
      • export const useI18N = () => useContext(I18NContext)
      • export const useI18n = useI18N (alias)
    2. Create a jscodeshift codemod to standardize imports:
      • Replace useContext(I18n) or import I18n from '...' with direct useI18N() hook.
      • Replace non-hook usage to named import: import { I18NContext } from '...' where context is needed.
    3. Add ESLint rule / codemod safety check to enforce useI18N hook.
    4. Run codemod in CI for mass update, review, and then remove the alias in a subsequent release.

Motivation

  • Improves maintainability and prevents regressions.
  • Reduces risk of inconsistent runtime behavior.
  • Enables centralized enforcement via ESLint and stricter TypeScript types.

Additional Context

Testing

  • Frontend local checks:
    • cd web
    • pnpm lint
    • pnpm test
  • Confirm translation strings show properly and unit tests for components that consumed useContext(I18n) pass.
  • Add unit tests for useI18N to verify fallback and alias behavior.

Additional Context

  • Related issues:
  • Files touched (examples):
    • web/context/i18n.ts
    • web/app/components/base/agent-log-modal/tool-call.tsx
    • web/app/components/tools/provider/* (20+ files)
  • Migration plan:
    1. Add alias and tests in one PR.
    2. Run codemod, fix linting, and open PR for codemod results for review.
    3. After one release, remove the alias and add stricter ESLint rule.
  • Small codemod example included in the PR to run locally and confirm changes:
    • jscodeshift transform will:
      • swap default import import I18n from '...' -> import { I18NContext } from '...' and then replace calls useContext(I18n) -> useI18N().

Example small alias change:

// filepath: /web/context/i18n.ts
import { createContext, useContext } from "react";

// existing code ...

export const I18NContext = createContext(/* default */ null);

// add stable alias for backward compatibility and migration
export const I18n = I18NContext; // alias for older import sites
export default I18NContext;

export const useI18N = () => useContext(I18NContext);
export const useI18n = useI18N; // alias

Codemod snippet (jscodeshift, minimal):

// filepath: /tools/codemods/standardize-i18n.js
export default function transformer(fileInfo, api) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  // Replace "useContext(I18n)" → "useI18N()"
  root.find(j.CallExpression, {
    callee: { name: 'useContext' },
    arguments: [{ name: 'I18n' }],
  }).forEach((p) => {
    j(p).replaceWith(j.callExpression(j.identifier('useI18N'), []));
  });

  // Replace "import I18n from 'web/context/i18n'" with alias removal
  root.find(j.ImportDefaultSpecifier, {
    local: { name: 'I18n' }
 }).forEach(p => {
    j(p).replaceWith(j.importSpecifier(j.identifier('I18NContext')));
  });

  return root.toSource();
}
Originally created by @morningstarxcdcode on GitHub (Nov 20, 2025). Originally assigned to: @morningstarxcdcode on GitHub. ### Self Checks - [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542). - [x] This is only for refactoring, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones. - [x] I confirm that I am using English to submit this report, otherwise it will be closed. - [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :) - [x] Please do not modify this template :) and fill in all the required fields. ### Description - Several files import i18n context with mixed names and patterns (I18N, I18n, useI18N, useI18n, useContext(I18n)). This inconsistency causes fragile imports, runtime errors, and adds maintenance burden. - Proposed refactor: 1. Add compatibility aliases in `web/context/i18n.ts`: - `export const I18NContext = createContext(...)` - `export const I18n = I18NContext` (alias) - `export const useI18N = () => useContext(I18NContext)` - `export const useI18n = useI18N` (alias) 2. Create a jscodeshift codemod to standardize imports: - Replace `useContext(I18n)` or `import I18n from '...'` with direct `useI18N()` hook. - Replace non-hook usage to named import: `import { I18NContext } from '...'` where context is needed. 3. Add ESLint rule / codemod safety check to enforce useI18N hook. 4. Run codemod in CI for mass update, review, and then remove the alias in a subsequent release. ### Motivation - Improves maintainability and prevents regressions. - Reduces risk of inconsistent runtime behavior. - Enables centralized enforcement via ESLint and stricter TypeScript types. ### Additional Context Testing - Frontend local checks: - cd web - pnpm lint - pnpm test - Confirm translation strings show properly and unit tests for components that consumed `useContext(I18n)` pass. - Add unit tests for `useI18N` to verify fallback and alias behavior. Additional Context - Related issues: - #27815, - #27582, - #27581, - #25194. - Files touched (examples): - web/context/i18n.ts - web/app/components/base/agent-log-modal/tool-call.tsx - web/app/components/tools/provider/* (20+ files) - Migration plan: 1. Add alias and tests in one PR. 2. Run codemod, fix linting, and open PR for codemod results for review. 3. After one release, remove the alias and add stricter ESLint rule. - Small codemod example included in the PR to run locally and confirm changes: - jscodeshift transform will: - swap default import `import I18n from '...'` -> `import { I18NContext } from '...'` and then replace calls `useContext(I18n)` -> `useI18N()`. Example small alias change: ```javascript // filepath: /web/context/i18n.ts import { createContext, useContext } from "react"; // existing code ... export const I18NContext = createContext(/* default */ null); // add stable alias for backward compatibility and migration export const I18n = I18NContext; // alias for older import sites export default I18NContext; export const useI18N = () => useContext(I18NContext); export const useI18n = useI18N; // alias ``` Codemod snippet (jscodeshift, minimal): ```javascript // filepath: /tools/codemods/standardize-i18n.js export default function transformer(fileInfo, api) { const j = api.jscodeshift; const root = j(fileInfo.source); // Replace "useContext(I18n)" → "useI18N()" root.find(j.CallExpression, { callee: { name: 'useContext' }, arguments: [{ name: 'I18n' }], }).forEach((p) => { j(p).replaceWith(j.callExpression(j.identifier('useI18N'), [])); }); // Replace "import I18n from 'web/context/i18n'" with alias removal root.find(j.ImportDefaultSpecifier, { local: { name: 'I18n' } }).forEach(p => { j(p).replaceWith(j.importSpecifier(j.identifier('I18NContext'))); }); return root.toSource(); } ```
yindo added the refactor label 2026-02-21 20:07:29 -05:00
Author
Owner

@morningstarxcdcode commented on GitHub (Nov 20, 2025):

I need some time so make the pull request

@morningstarxcdcode commented on GitHub (Nov 20, 2025): I need some time so make the pull request
Author
Owner

@crazywoola commented on GitHub (Nov 21, 2025):

Good job, take your time :)

@crazywoola commented on GitHub (Nov 21, 2025): Good job, take your time :)
Author
Owner

@DavideDelbianco commented on GitHub (Nov 21, 2025):

the same thing happens with the ToastProvider

@DavideDelbianco commented on GitHub (Nov 21, 2025): the same thing happens with the ToastProvider
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#20447