mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 01:22:23 +08:00
* feat: add IBM Globalization Pipeline integration and i18n setup
- Add GP REST API client with GP-HMAC authentication (scripts/gp/gp_client.py)
- Add upload/download scripts for syncing strings with GP
- Set up i18next + react-i18next with 7 language support (en, fr, ja, es, de, pt, zh-Hans)
- Add 48 UI strings in flat dot notation to en.json (from alerts_constants)
- Add GitHub Actions for automated upload on en.json changes and daily translation download
* [autofix.ci] apply automated fixes
* fix: resolve ruff linting issues in GP scripts
- Fix quote style, import ordering, docstring format
- Use Path instead of os.makedirs/open/os.path.join
- Add missing timeouts to requests calls
- Move noqa comments to correct lines for S501
* feat: migrate all frontend UI strings to i18n for GP localization
- Replace all hardcoded user-facing strings with t() calls across 80+ components, pages, modals, and utilities
- Add 326 translation keys to en.json covering errors, alerts, dialogs, chat, flow, settings, store, table, output, nav, auth, and more
- Add translated locale files for fr, es, de, pt, ja, zh-Hans (downloaded from Globalization Pipeline)
- Migrate alerts_constants.tsx and constants.ts string usages to react-i18next
- Support non-React files (stores, utils) via i18n.t() direct calls
* feat: migrate sidebar strings to i18n (Phase 4)
- Add 56 new translation keys for sidebar categories, nav items, labels, buttons, and empty states
- Convert SIDEBAR_CATEGORIES display_names to i18n key references in styleUtils.ts
- Update 13 sidebar components to use t() for all user-facing strings
- Refresh all 6 locale files from GP (382 total keys)
- Add BACKEND_STRINGS_STRATEGY.md documenting plan for component display_name i18n
* [autofix.ci] apply automated fixes
* fix: add react-i18next mock and fix sidebar nav tests for i18n migration
- Add global react-i18next mock in jest.setup.js using en.json lookups so
t(key) returns English strings instead of raw keys in test environment
- Fix sidebarSegmentedNav tests: update NAV_ITEMS expectations to use i18n
keys, and use en.json lookups for tooltip/accessibility label assertions
* feat: add LanguageSelector dropdown to globalization pipeline
Cherry-picked frontend-only changes from feat/gp-backend-i18n (6ab21559db, 3e274a84e3):
- Add LanguageSelector component with dropdown to switch UI language
- Wire LanguageSelector into AccountMenu header
- Persist language preference to localStorage in i18n.ts
- Send Accept-Language header via useCustomApiHeaders (reactive via useTranslation)
No backend files included.
* [autofix.ci] apply automated fixes
* ci: add GP_TEST environment to gp-download and gp-upload workflows
* temp: add feature branch push trigger for workflow testing
* temp: trigger gp-upload workflow test
* fix: use correct GP_test environment name in workflows
* fix: use vars for GP_INSTANCE and GP_BUNDLE
* [autofix.ci] apply automated fixes
* fix: correct environment name to GP-test (hyphen not underscore)
* chore: update translations from Globalization Pipeline [skip ci]
* [autofix.ci] apply automated fixes
* chore: update translations from Globalization Pipeline [skip ci]
* temp: trigger gp-upload workflow test
* ci: add download-gp-bundle job to nightly build pipeline
Downloads frontend translations from Globalization Pipeline after
create-nightly-tag and before frontend tests run, ensuring tests
always validate the latest translated locale files.
- Frontend tests (Linux/Windows) now depend on download-gp-bundle
- Backend unit tests are unaffected (no frontend locale dependency)
- GP failure blocks release-nightly-build (same severity as linux tests)
* chore: remove temporary dev files from gp scripts directory
Remove BACKEND_STRINGS_STRATEGY.md (internal planning doc), test_auth.py
(GP auth debugging script), and en.json (dev-time string snapshot).
Also add venv/ to .gitignore to prevent accidental commits of the local virtualenv.
* [autofix.ci] apply automated fixes
* chore: update translations from Globalization Pipeline [skip ci]
* [autofix.ci] apply automated fixes
* chore: update translations from Globalization Pipeline [skip ci]
* feat(i18n): move language selector from account menu to Settings > General
- Add LanguageForm card component in GeneralPage following the existing
Card pattern (ProfilePictureForm), with immediate language switching
- English marked as "(Recommended)" via i18n key
- Remove Language row from AccountMenu dropdown
- Add settings.languageTitle/Description/Recommended keys to en.json
for upload to Globalization Pipeline
* feat(i18n): refactor language constants and improve GP pipeline
- Extract SUPPORTED_LANGUAGES into src/frontend/src/constants/languages.ts
- Update LanguageForm to import from shared constants, add aria-label
- Add settings.languageSelectAriaLabel key to en.json
- Update LanguageSelector component to use shared SUPPORTED_LANGUAGES
- Fix gp_client and download_translations scripts
- Update GP upload/download/nightly GitHub Actions workflows
- Add GP script tests
* [autofix.ci] apply automated fixes
* ci(gp): trigger upload on release branches, download via dispatch only
- Upload now triggers on push to release-* branches (instead of main)
so translators get strings once they're finalized on the release branch
- Download removes the daily cron schedule; workflow_dispatch only
since the nightly build already handles scheduled runs
* chore: update translations from Globalization Pipeline
* fix(i18n): address PR review comments — tests, nightly build decoupling, GP test workflow
- Add pytest tests for gp_client.py (HMAC auth, HTTP errors, timeout)
- Add pytest tests for upload_strings.py and download_translations.py
- Add conftest.py to set sys.path so GP scripts can be imported from repo root
- Add scripts/gp/__init__.py to make gp a proper package for test imports
- Add gp-test.yml workflow to run GP script tests on PRs touching scripts/gp/**
- Decouple download-gp-bundle from nightly build chain:
- Add continue-on-error: true so GP failure never blocks the build
- Remove download-gp-bundle from needs/if of frontend-tests-linux,
frontend-tests-windows, and release-nightly-build
- Add warning annotation step that fires on GP download failure
* feat(i18n): lazy-load non-English locale files via Vite dynamic imports
- Remove 6 static locale imports from i18n.ts; only en.json is bundled
- Export loadLanguage() which dynamically imports a locale chunk on demand
and caches it via i18n.hasResourceBundle (no-op on repeat calls)
- Update index.tsx to await loadLanguage(detectedLang) before rendering,
preventing an English flash for non-English users
- Make handleChange async in LanguageForm and LanguageSelector to await
loadLanguage before calling i18n.changeLanguage
- Add i18n.test.ts covering loadLanguage: en no-op, new language load,
cache hit, and multiple independent languages
- Update LanguageForm.test.tsx to mock @/i18n and assert loadLanguage
is called before changeLanguage on language switch
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* fix: add ruff ignores for scripts/gp/tests
- Add per-file ignores for scripts/gp/tests/* to fix CI failures
- Ignore S101 (assert usage), TRY003, EM101 for test files
- Follows existing pattern for other test directories
* [autofix.ci] apply automated fixes
* fix(ci): run GP translation download before nightly tag creation
Previously download-gp-bundle ran after create-nightly-tag, so fresh
translations were committed to the branch after the tag was already
created. The nightly Docker image builds from the tag, meaning it never
included the day's translations.
Fix: move download-gp-bundle to run before create-nightly-tag so the
tag is created on a commit that already includes the latest translations.
* fix(ui): replace native select with Radix UI Select in LanguageForm
Swaps the native <select> element for the existing Radix UI Select
component to get consistent padding on both sides of the chevron,
removing reliance on browser-rendered chevron positioning.
* feat: backend i18n — serve translated component metadata via Accept-Language
- Add locale middleware (set_locale) in main.py to parse Accept-Language header
- Add translate_component_dict() in utils/i18n.py to substitute display_names
- Add backend locale files (en/fr/es/de/pt/ja/zh-Hans) with 4,748 keys
- Add GP scripts: extract_backend_strings.py, upload_backend_strings.py, download_backend_translations.py
- Add CI workflows: gp-backend-check.yml (PR validation + auto-commit), gp-backend-upload.yml (release branch upload)
- /api/v1/all returns translated component metadata for non-English locales
* chore: auto-regenerate backend locales/en.json [skip ci]
* chore: auto-regenerate backend locales/en.json [skip ci]
* refactor: simplify GP backend workflows to remove duplication
- gp-backend-check: drop redundant --check job, keep only auto-commit
- gp-backend-upload: remove extraction/commit steps, trigger on en.json
path change, and add latest-release-branch guard (mirrors gp-upload.yml)
* feat: add nightly backend translation download job to gp-download workflow
Mirrors the existing frontend download job — resolves latest release branch,
runs download_backend_translations.py, and opens an auto-merge PR.
* [autofix.ci] apply automated fixes
* fix: skip backend translation download on nightly schedule
Backend download only runs on workflow_dispatch — the nightly cron is
for the frontend build pipeline only.
* fix: remove backend GP download from nightly build workflow
Backend translations are now downloaded via workflow_dispatch in
gp-download.yml, not as part of the nightly build pipeline.
* fix: remove frontend GP download from nightly build workflow
Frontend translations are now downloaded via nightly cron in
gp-download.yml, not as part of the nightly build pipeline.
* fix: remove en.json from check workflow trigger paths
The extraction script only reads from lfx.components — en.json is never
edited directly, so triggering on it was redundant and would silently
overwrite any intentional manual edits.
* fix: mock lfx.components in sys.modules for collect_strings test
The test was failing because collect_strings() imports lfx.components
before reaching the pkgutil/importlib mocks. Injecting fake modules into
sys.modules lets the import succeed so the mocks can take over.
* fix: resolve ruff violations in GP scripts and main.py
- Use .values() instead of .items() when attr_name is unused (B007/PERF102)
- Split long print f-string to stay under 120 chars (E501)
- Add blank line in docstring between summary and description (D205)
- Use ternary operator for locale assignment in main.py (SIM108)
* feat(i18n): sync canvas node translations on language change
Add syncNodeTranslations() to flowStore that patches translatable
metadata (display_name, description, template field display_names,
output display_names) on all canvas nodes when the types catalog is
refreshed with a new language. Field values are never touched. Nodes
whose type is absent from the catalog (custom-code, group nodes) are
skipped.
Called from two places to handle the race between flow loading and
types fetching:
- use-get-types.ts after setTypes(data) — covers the case where nodes
are already on canvas when translations arrive
- resetFlow — covers the case where translations are already loaded
when the flow mounts
* feat(i18n): translate Templates modal and starter flow metadata
## Backend
- Extend extraction script (`scripts/gp/extract_backend_strings.py`) to
auto-discover all `initial_setup/starter_projects/*.json` files and emit
`starter_flows.{safe_key}.name` / `starter_flows.{safe_key}.description`
keys — no manual work needed when new starter flows are added.
- Add `translate_starter_flows()` to `utils/i18n.py`, mirroring the
existing `translate_component_dict()` pattern. Derives a stable
`name_key` (slug) from the English name and attaches it to each
`FlowRead` so the frontend can match flows by identity regardless of
locale.
- Update `FlowRead` model to expose `name_key: str | None`.
- Refactor `read_basic_examples()` endpoint (`api/v1/flows.py`):
- Accept `Request` parameter to read `request.state.locale` (set by
existing `set_locale` middleware).
- Cache the raw `flow_reads` list instead of the compressed response so
the same DB result can be translated per-request (cheap: ~33 dict
lookups, no additional DB hit).
- Call `translate_starter_flows()` on every request so `name_key` is
always populated.
- Update all 7 backend locale files (en, fr, de, es, ja, pt, zh-Hans)
with new `starter_flows.*` keys from GP.
## Frontend
- Templates modal (`modals/templatesModal/`):
- Translate sidebar title, category group labels ("Use Cases",
"Methodology"), and all nav item labels.
- Translate "Get started" header and description.
- Translate card category badges (PROMPTING, RAG, AGENTS).
- Translate search placeholder and "No templates found" empty state.
- Translate "Start from scratch" footer and "Blank Flow" button.
- Use `flow.name_key` (stable slug) for example lookup in
`GetStartedComponent` so matching is locale-independent.
- Refetch examples on language change: add `i18n.language` to the
`useGetBasicExamplesQuery` cache key so React Query re-fetches
translated flow data whenever the UI language switches.
- Add `name_key?: string | null` to `FlowType`.
- Empty state pages (`emptyPage`, `emptyFolder`): translate "Empty
project", "Start building", description, and "New Flow" button.
- Update all 7 frontend locale files with new `templatesModal.*` and
`emptyPage.*` keys from GP.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* feat(i18n): translate MCP Server tab and fix sidebar category truncation
- Translate all strings in McpServerTab, McpFlowsSection, McpAuthSection:
title, description, guide link, Flows/Tools label + tooltip, Edit Tools
button, Auth label, None (public), Edit/Add Auth, Auto install tab,
Loading state, config error messages
- Use stable id/label split for Auto install / JSON tabs so mode
comparison is locale-independent
- Add truncate to sidebar category label span to prevent long translated
names (e.g. French "Mannequins et agents") from wrapping to two lines
- Update all 7 frontend locale files with new mcp.* keys from GP
* feat(i18n): translate Knowledge, Files pages and fix sidebar overflow
- Translate Knowledge page (empty state, search, loading, alerts, chunks page)
- Translate My Files page (title, empty state, table columns, upload/delete)
- Fix sidebar category label overflow with min-w-0 truncate on flex spans
- Fix "Discover more components" button text truncation
- Fix empty page CTA button max-width for longer translations
- Remove duplicate language selector from account menu (browser auto-detection already in i18n.ts)
- Upload 471 strings to GP and download translations for fr/ja/es/de/pt/zh-Hans
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* feat(i18n): translate delete confirmation modal and fix CTA button overflow
- Translate DeleteConfirmationModal (title, body, buttons, "can't be undone")
- Translate flow/folder/component description labels via use-description-modal
- Translate "and its message history" and folder delete note at call sites
- Fix empty page CTA button overflow with whitespace-normal w-auto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate Create Knowledge Base modal and flow dropdown menu
* [autofix.ci] apply automated fixes
* fix(i18n): use min-height for Knowledge Base modal to prevent scroll on translated text
* [autofix.ci] apply automated fixes
* feat(i18n): translate StepperModal footer buttons (Next Step, Back, Need Help?)
* feat(i18n): translate model input dropdown (No Models Enabled, Refresh List, Manage Model Providers)
* feat(i18n): translate Model Providers modal (config form, model selection, disconnect warning, multiselect placeholder)
* feat(i18n): translate starter template note/README nodes at serve-time
- Extend extract_backend_strings.py to extract noteNode descriptions as
template_notes.{flow_key}.{index} keys (64 notes across 32 templates)
- Add translate_flow_notes() to i18n.py to substitute translated markdown
per-request without mutating the cached template data
- Wire translate_flow_notes() into the /starter-projects/ endpoint using
the same request.state.locale pattern as the flows endpoint
- Update all 6 backend locale files via GP pipeline (4858 keys total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate canvas note nodes on language change via dedicated endpoint
- Add stamp_note_keys() to i18n.py: stamps i18n_key onto noteNodes of
starter templates when a user copy is created, enabling stable lookup
- Call stamp_note_keys() in _new_flow() (flows_helpers.py) so keys are
persisted to DB at copy-creation time
- Add GET /flows/{flow_id}/note_translations endpoint returning
{ node_id: translated_text } for the request locale; falls back to
positional recompute for flows without stamped keys
- Add useGetNoteTranslationsQuery (React Query) keyed on [flowId, language]
so it auto-refetches on language change without explicit invalidation
- Apply translations in NoteNode via useEffect on query data
- Rewrite syncNoteTranslations to accept Record<string, string> (node_id map)
- Remove old syncNoteTranslations(FlowType[]) approach and all debug
console.warn/console.error [i18n-debug] statements
- Suppress i18next Locize promotional console.info by temporarily replacing
console.info around the synchronous init() call
- Add i18n_key?: string to noteClassType frontend type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): fix Playground/Share button overlap and translate Share button
- Remove fixed w-[7.2rem] from SimpleSidebarTrigger and DisabledButton so
the Playground button grows to fit translated text in any language
- Add misc.share i18n key and replace hardcoded "Share" string in
deploy-dropdown with t("misc.share")
- Download GP translations for de/pt/zh-Hans
* feat(i18n): translate input placeholder and info fields for backend components
Extend the backend i18n pipeline to cover placeholder and info (tooltip)
text in addition to display_name and description:
- extract_backend_strings.py: extract placeholder from input fields
- i18n.py (translate_component_dict): translate placeholder and info at
request time alongside display_name
- Regenerate en.json (6700 keys, +179 placeholders, +info keys)
- Download translated locale files for de, es, fr, ja, pt, zh-Hans
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate Share dropdown items (API access, Export, MCP Server, Embed, Shareable Playground)
* fix(i18n): stamp i18n_key in translate_flow_notes and remove dead stamp_note_keys
- Stamp i18n_key onto noteNodes inside translate_flow_notes() using the
original English name_key so the key travels with the node when a user
copies a template — fixes translation failure for duplicate copies like
"Simple Agent (2)" where the frontend sends the translated name
- Remove stamp_note_keys() and its call in _new_flow(): now a no-op since
i18n_key is always stamped by translate_flow_notes at /basic_examples/
- Remove positional fallback from get_note_translations: only use stamped
i18n_key; drop unused _safe_flow_key import and note_index counter
- Fix read_basic_examples to pass name_key (original English) instead of
the already-translated flow.name to translate_flow_notes
* refactor(i18n): bake i18n_key into template noteNodes, remove runtime stamping
Replaces fragile positional indexing with keys baked directly into the source
JSON files, so keys are stable regardless of node order changes.
- scripts/gp/bake_note_keys.py: new script that stamps template_notes.{flow}.{n}
onto each noteNode missing an i18n_key; idempotent and --dry-run safe
- scripts/gp/extract_backend_strings.py: Tier 4 reads i18n_key from node data
instead of positional index; warns if any noteNode is missing a key
- utils/i18n.py: translate_flow_notes() simplified — reads baked key, translates,
no more flow_name param or index counter or stamping logic
- api/v1/flows.py, starter_projects.py: drop flow_name arg from call sites
- test_i18n_note_translation.py: tests updated for key-read behavior
- starter_projects/*.json: 64 noteNodes across 32 templates stamped with i18n_key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci(i18n): auto-bake note keys on template JSON changes in CI
Extends gp-backend-check.yml to trigger on starter_projects/** changes
and runs bake_note_keys.py before extract_backend_strings.py so any new
noteNodes get i18n_key stamped automatically on PR branches.
* refactor(i18n): replace index-based note keys with content-hash keys
bake_note_keys.py now derives i18n_key from sha256[:8] of the
noteNode description instead of a positional counter. Keys are
stable on reorder, auto-rotate when content changes, and never
silently reuse an old index after a note is deleted.
All 64 note keys across 32 starter templates restamped to the
new template_notes.{flow_key}.{hash8} format. Script is idempotent —
running twice on unchanged content produces no changes.
* fix(i18n): thread-safe translation loading and correct falsy key check
- _load_translations(): add double-checked locking with threading.Lock to
prevent race condition under concurrent requests at cold start
- translate(): use `is not None` instead of truthiness check so empty-string
translations are not silently dropped in favour of the fallback chain
* feat(i18n): translate Settings page UI strings across multiple sections
- Profile picture chooser: translate People/Space category labels
- MCP Servers page: translate title, description, button, status labels, dropdown items, and all Add MCP Server modal strings
- API Keys page: translate column headers (Name, Key, Created, Last Used, Total Uses) and Create API Key dialog (title, description, labels, buttons, generated key message)
- Shortcuts page: translate all 27 shortcut display names via valueFormatter
- Messages page: translate title and description
- Base modal: translate shared Cancel button
* fix(i18n): replace fixed card heights with min-h to prevent layout issues with longer translations
Cards with h-[84px] would overflow/misalign when translated text wrapped to
multiple lines. Using min-h-[84px] keeps the minimum size while allowing
cards to grow naturally for longer translations.
* feat(i18n): translate SaveChangesModal (unsaved changes popup)
Translates all strings in the unsaved-changes dialog shown when
navigating away from a flow with pending changes:
- title, saving spinner, last saved, exit/save buttons
- unsaved changes warning and auto-saving help link
* chore(i18n): download updated frontend and backend translations from GP
Frontend: 704 strings across fr, ja, es, de, pt, zh-Hans
Backend: updated note keys (hash-based) across fr, ja, es, de, pt, zh-Hans
* feat(i18n): translate ToolsModal (URL/tools selection popup)
Translates all strings in the tools modal including:
- search placeholder, column headers (Name, Description, Slug/Tool)
- sidebar labels, input placeholders, hint text
- Parameters section title and subtitle
- Close button
* chore(i18n): download updated frontend translations from GP
724 strings across fr, ja, es, de, pt, zh-Hans including new
toolsModal and saveChangesModal keys.
* feat(i18n): translate MCP Client settings page, shareable playground label, and Global Variable modal
- McpClientPage: add useTranslation, move all hardcoded strings (title,
description, steps, config file label, API key notice) to
settings.mcpClient.* keys
- deploy-dropdown: fix unpublished "Shareable Playground" label that was
missed when the published branch already used t()
- GlobalVariableModal: add useTranslation, translate all UI strings
(title, description, type/name/value/fields labels & placeholders,
save/update button, success and error toasts)
- en.json: add 13 settings.mcpClient.* keys and 23 globalVars.modal.* keys
- All locale files (de, es, fr, ja, pt, zh-Hans) updated from GP
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate MCP Server Auth modal (authModal)
- Add useTranslation to authModal/index.tsx
- Translate all hardcoded strings: title, auth type label, radio labels
(None/API Key/OAuth), no-auth warning, API Key description, OAuth
field labels/placeholders, Save button, reinstall warning (with and
without installed clients interpolation)
- Add 30 authModal.* keys to en.json
- Update all locale files (de, es, fr, ja, pt, zh-Hans) from GP (776 strings)
* feat: add translation keys for MCP JSON configuration
* feat(i18n): translate project delete notification, MCP install notification, and McpJsonContent
- main-page.tsx: translate "Project deleted successfully" and "Error
deleting project" success/error toasts
- useMcpServer.ts: translate "MCP Server installed successfully on
{{client}}..." success toast using i18n.t()
- McpJsonContent.tsx: translate "Add this config..." hint, Transport
label, and Generate/API key generated button labels
- Add 3 new keys (mcp.installedSuccessfully, project.deletedSuccessfully,
project.errorDeleting) + 5 mcpJson.* keys to en.json
- Update locale files from GP
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate default flow/project names and delete notifications
- reactflowUtils.ts: use i18n.t("flow.defaultName") for new flow default name
- use-add-flow.ts: use t() for "Starter Project" and "New Project" names at creation time
- sideBarFolderButtons: use t("project.newName") when creating a new project
- main-page.tsx: translate project deleted success/error toasts
- useMcpServer.ts: translate MCP install success toast
- list/index.tsx: translate flow deleted success/error toasts
- Add keys: flow.defaultName, project.newName, project.starterName,
project.deletedSuccessfully, project.errorDeleting,
flow.deletedSuccessfully, flow.errorDeleting, flow.errorDeletingRetry,
mcp.installedSuccessfully
- Update all locale files from GP (790 strings)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(i18n): content-hash component keys and normalize frontend lookup
Backend locale keys for components now use a hybrid format:
components.{norm_name}.{field_path}.{sha256[:8]}
e.g. components.prompttemplate.display_name.8cd80ebe
The 8-char hash suffix is derived from the English source value, so any
change to a component string produces a new key, forcing GP to issue a
fresh translation. The human-readable prefix keeps keys debuggable.
Component names are normalized (spaces removed, lowercased) in both the
key prefix and the runtime hash computation, so renames like
"PromptTemplate" → "Prompt Template" don't break existing translations.
Frontend syncNodeTranslations() now builds a normalized lookup map so
that nodes stored with old-style type names (e.g. "PromptTemplate") are
correctly resolved against the live registry key ("Prompt Template"),
fixing untranslated Prompt Template nodes in starter template flows.
Also adds chunked uploading to upload_backend_strings.py to avoid
read timeouts when pushing large payloads to GP.
* feat(i18n): fix legacy component alias translation and translate file manager UI
Frontend:
- syncNodeTranslations() now falls back to templates store to resolve legacy
node.data.type aliases (e.g. "Prompt" → Prompt Template, "parser" →
ParserComponent), fixing untranslated nodes in starter templates like
Vector Store RAG
- Translate "Flow saved successfully!" toast in FlowPage
- Translate file manager modal: "Select files" button, error/success toasts,
drag-and-drop zone labels, folder selection strings (13 new keys)
Backend:
- check_backend_status.py: new script to check GP translation progress
- upload_backend_strings.py: single PUT with 5min timeout, bundle name logged
- download_backend_translations.py: bundle name logged on start
- All locale files updated with latest GP translations (langflow-ui-backend-v2)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(i18n): translate canvas controls and Model Providers page strings
- Add useTranslation to MemoizedCanvasControls for Flow Locked, Agent Working, Saving, Lock/Unlock flow labels
- Add useTranslation to CanvasControlsDropdown for Zoom In/Out/To 100%/To Fit and tooltips
- Add useTranslation to ModelProvidersPage for page title and description
- Add canvas.* and modelProviders.page* keys to en.json
- Sync all locale files (fr, ja, es, de, pt, zh-Hans) via GP pipeline
* feat(i18n): translate upload folder button and model trigger strings
- UploadFolderButton: replace hardcoded "Upload a flow" tooltip with t("folder.uploadFlow")
- ModelTrigger: replace hardcoded "No models enabled" and "Select a model" with t() calls
* feat(i18n): cache /basic_examples/ translations per locale
Add _starter_flows_translated_cache keyed by locale so translate_starter_flows
and translate_flow_notes run at most once per locale per 5 minutes instead of
on every request.
* feat(i18n): translate NoteNode empty placeholder text
* feat(i18n): translate slider labels, model trigger, dropdown loading, and loading component
- Translate Precise/Balanced/Creative/Wild slider labels and min/max labels
- Translate "Setup Provider" placeholder in ModelTrigger
- Translate "Loading options" in dropdownComponent (both occurrences)
- Translate "Loading..." in loadingComponent (used by LoadingPage)
- Add syncNodeTranslations() call after model refresh to re-apply translations
- Add 8 new keys to en.json and download translated locale files
* feat(i18n): translate model provider toast notifications
Replace hardcoded English strings in useProviderConfiguration with t()
calls: configuration saved, activated, disconnected success toasts, and
all error titles/messages for save, activate, disconnect, and model
toggle operations.
* fix(i18n): address PR review — DRY key functions, locale allow-list, dead code, silent failures
- Extract safe_flow_key/normalize_component_key/content_hash/component_field_key into
langflow/utils/i18n_keys.py so runtime translator and extract script share one
source of truth (C1)
- Validate Accept-Language against supported locales in set_locale middleware;
unknown values fall back to "en" to prevent cache pollution (C2)
- Delete dead `remaining = ...` block in check_backend_status.py that made a
redundant network call and never used the result (I2)
- Log logger.warning instead of silently swallowing OSError/JSONDecodeError in
_load_translations (I3)
- Rename test_strips_dedup_suffix → test_parentheses_in_name_become_underscores
with corrected comment (I5)
- Remove redundant `import copy as _copy` inside translate_flow_notes (R2)
- Move translate_flow_notes/translate_starter_flows import to flows.py module top (R1)
- Add # Why: comments on max_size=16, uncompressed cache, and verify=False (R5/I8/M3)
- Wrap loadLanguage dynamic import in try/catch so unknown locales don't crash
React with an unhandled rejection; add i18n.test.ts to cover the fallback
* refactor(gp): consolidate upload/download scripts with --target flag
Replaces four separate scripts (upload_strings, upload_backend_strings,
download_translations, download_backend_translations) with two unified
entry points: upload.py and download.py, each taking --target frontend|backend.
Updates gp-upload.yml to a single workflow with two jobs, updates
gp-download.yml run lines, and removes the now-redundant
gp-backend-upload.yml workflow.
* [autofix.ci] apply automated fixes
* chore(starter-projects): sync starter project snapshots with release-1.10.0
Updates dependency versions (langchain_core 1.2.29 → 1.2.27) and minor
JSON formatting differences across 21 starter project files following
merge from release-1.10.0.
* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]
* feat(i18n): restore LanguageForm component in Settings > General page
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* fix(projects): always use English for new project names
Translated project names (e.g. Japanese '新規プロジェクト') caused MCP
server name collisions because sanitize_mcp_name() strips non-ASCII
chars and all names fell back to 'lf-unnamed'. Hardcode 'New Project'
and 'Starter Project' instead of passing them through t() so the
names sent to the backend are always ASCII.
* chore(i18n): download missing Vector Store RAG note translations
README and Load Data Flow note keys were missing from all non-English
locales. Uploaded to GP and downloaded translations for fr, ja, es,
de, pt, zh-Hans.
* [autofix.ci] apply automated fixes
* fix(canvas): fix zoom dropdown shortcut layout on Windows
The shortcut container had a fixed w-[25px] which fits the single-char
Mac modifier key (⌘) but clips the 4-char Windows modifier key (Ctrl).
Remove the fixed width and use gap-0.5 so the container sizes to its
content on all platforms.
* fix(i18n): sync tooltip info and placeholder fields for existing canvas nodes
syncNodeTranslations() was only updating display_name from fresh component
definitions, leaving baked-in English info (tooltip) and placeholder strings
on nodes loaded from saved flows and starter templates. Extend the merge to
also overlay info and placeholder for template fields, and info for output
fields. Also adds info?: string to OutputFieldType to match what the backend
already sends.
* fix(i18n): persist language selection across page refreshes
loadLanguage() was only adding the resource bundle but never calling
i18n.changeLanguage(), so the active language stayed "en" on every page
load even when a preference was saved in localStorage. Move the
changeLanguage() call into loadLanguage() so both startup (index.tsx)
and runtime language switches activate the language after loading the bundle.
* fix(sidebar): add tooltip to truncated category header labels
Category headers use truncate but had no tooltip, making long translated
names (e.g. French) unreadable. Wrap the label span with ShadTooltip
matching the pattern used by sidebar draggable items.
* fix(sidebar): add tooltip to truncated discover more components button
* fix(sidebar): add tooltips to footer buttons (new custom component, MCP)
* fix(login): center-align title to handle wrapped translations gracefully
* fix(signup): center-align title to handle wrapped translations gracefully
* fix: remove tooltips from form field labels and add tooltip to sign-in button for consistency
* [autofix.ci] apply automated fixes
* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]
* [autofix.ci] apply automated fixes
* fix: resolve ruff violations in gp scripts and i18n util
- FBT001: make dry_run keyword-only in bake_note_keys._bake_file
- S112: add S112 to noqa comments in extract_backend_strings.py
- PLW2901: rename shadowed loop variable in i18n.translate_note_nodes
* [autofix.ci] apply automated fixes
* fix: lazy-import langflow in extract_backend_strings to fix GP test collection
The top-level `from langflow.utils.i18n_keys import ...` caused an
ImportError in CI where the GP test job runs without langflow installed.
Move the imports inside collect_strings() so the module is importable
without langflow — all tests mock collect_strings anyway.
* fix(tests): mock langflow.utils.i18n_keys in test_collect_strings_skips_deactivated_modules
The test calls collect_strings() directly, which now lazy-imports
langflow.utils.i18n_keys. Patch a minimal fake module into sys.modules
so the test runs without langflow installed. Also fix the key assertion
to match the lowercased output of normalize_component_key.
* fix(frontend): restore missing modal height constant imports in KnowledgeBaseUploadModal
The i18n commit (bd070cab63) accidentally dropped MODAL_HEIGHT_DEFAULT,
MODAL_HEIGHT_WITH_ADVANCED, and VALIDATION_ERROR_LINE_HEIGHT from the
constants import when refactoring STEP_TITLES/STEP_DESCRIPTIONS, causing
a ReferenceError at runtime and failing all 39 Jest tests.
* [autofix.ci] apply automated fixes
* fix(tests): add global mocks for ShadTooltip and useGetNoteTranslationsQuery
- Mock shadTooltipComponent to render children only — avoids needing
TooltipProvider in sidebar unit tests
- Mock useGetNoteTranslationsQuery to return empty data — avoids needing
QueryClientProvider in NoteNode unit tests
- Fix t() interpolation mock to resolve {{count}} in KnowledgeBasesTab tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): add syncNodeTranslations no-op mock to flowStore in affected tests
* [autofix.ci] apply automated fixes
* fix(tests): use stable testId selector instead of node title text in Blog Writer spec
* fix(i18n): preserve instance-level node display_name and description in syncNodeTranslations
Only sync field-level translations (template inputs and outputs).
Node titles and descriptions are instance-level identity — user and
template renames must not be overwritten by canonical registry values.
* chore: auto-bake note keys and regenerate backend locales/en.json [skip ci]
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
"""Bake i18n_key into noteNodes in starter template JSON files.
|
|
|
|
For each noteNode in every starter project JSON:
|
|
- Compute the expected key: template_notes.{flow_key}.{sha256[:8]}
|
|
where the hash is derived from the noteNode's description field.
|
|
- If data.node.i18n_key already equals the expected key: leave it as-is.
|
|
- Otherwise (missing or stale after an edit): assign the expected key.
|
|
|
|
This means keys are content-addressed — they change automatically when the
|
|
description changes, stay stable when nodes are reordered, and never silently
|
|
collide when a note is deleted and a new one added at the same position.
|
|
|
|
en.json is managed exclusively by extract_backend_strings.py — run that after
|
|
baking to pick up any new or changed note keys.
|
|
|
|
Usage (from repo root, no virtualenv required):
|
|
python scripts/gp/bake_note_keys.py
|
|
python scripts/gp/bake_note_keys.py --dry-run # preview without writing
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).parent.parent.parent
|
|
STARTER_PROJECTS_DIR = REPO_ROOT / "src/backend/base/langflow/initial_setup/starter_projects"
|
|
|
|
# NOTE: _safe_flow_key and _note_hash are intentionally kept inline (not imported from
|
|
# langflow.utils.i18n_keys) because this script is designed to run WITHOUT a virtualenv —
|
|
# the CI workflow calls it before the Python environment is set up. bake_note_keys.py
|
|
# only writes template_notes.{key} values that are read back verbatim by i18n.py at
|
|
# runtime, so a local drift here does NOT affect component-translation correctness.
|
|
|
|
|
|
def _safe_flow_key(name: str) -> str:
|
|
return re.sub(r"[^a-zA-Z0-9]+", "_", name).strip("_").lower()
|
|
|
|
|
|
def _note_hash(description: str) -> str:
|
|
return hashlib.sha256(description.encode()).hexdigest()[:8]
|
|
|
|
|
|
def _bake_file(path: Path, *, dry_run: bool) -> int:
|
|
"""Bake i18n_keys into a single template JSON file. Returns number of keys added/updated."""
|
|
with path.open(encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
name = data.get("name") or path.stem
|
|
flow_key = _safe_flow_key(name)
|
|
nodes = data.get("data", {}).get("nodes", [])
|
|
|
|
keys_changed = 0
|
|
|
|
for node in nodes:
|
|
if node.get("type") != "noteNode":
|
|
continue
|
|
node_data = node["data"]["node"]
|
|
description = node_data.get("description", "")
|
|
expected = f"template_notes.{flow_key}.{_note_hash(description)}"
|
|
|
|
if node_data.get("i18n_key") == expected:
|
|
continue
|
|
|
|
action = "updating" if "i18n_key" in node_data else "assigning"
|
|
if not dry_run:
|
|
node_data["i18n_key"] = expected
|
|
print(f" + {path.name}: {action} {expected!r}")
|
|
keys_changed += 1
|
|
|
|
if keys_changed > 0 and not dry_run:
|
|
with path.open("w", encoding="utf-8") as f:
|
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
f.write("\n")
|
|
|
|
return keys_changed
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
parser.add_argument("--dry-run", action="store_true", help="Print changes without writing files")
|
|
args = parser.parse_args()
|
|
|
|
if args.dry_run:
|
|
print("DRY RUN — no files will be modified.\n")
|
|
|
|
total_changed = 0
|
|
for project_file in sorted(STARTER_PROJECTS_DIR.glob("*.json")):
|
|
total_changed += _bake_file(project_file, dry_run=args.dry_run)
|
|
|
|
template_count = len(list(STARTER_PROJECTS_DIR.glob("*.json")))
|
|
print(f"\nBaked {total_changed} i18n_key(s) across {template_count} templates.")
|
|
if total_changed > 0 and not args.dry_run:
|
|
print("Run extract_backend_strings.py to update en.json.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|