Skip to content

feat: decouple --lang preference from TUI display language#1132

Merged
liangshuo-1 merged 25 commits into
mainfrom
feat/cliconfig-lang-multilang
May 28, 2026
Merged

feat: decouple --lang preference from TUI display language#1132
liangshuo-1 merged 25 commits into
mainfrom
feat/cliconfig-lang-multilang

Conversation

@luozhixiong01

@luozhixiong01 luozhixiong01 commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

The config init / config bind --lang flag was overloaded: it both selected the TUI display language and set a persistent preference, sharing one opts.Lang field. This produced a contradictory UX (the picker offered languages the TUI then fell back to Chinese for) and let unvalidated values reach disk. This PR splits the two concerns: --lang becomes a strictly-validated persistent preference, while the TUI is bilingual (zh/en) driven by a separate field. The supported preference set is aligned with the Feishu client UI (14 languages).

Changes

  • Add UILang field to BindOptions / ConfigInitOptions (cmd/config/bind.go, cmd/config/init.go); TUI rendering reads UILang (default zh, picker-only), preference writes read Lang
  • Strictly validate --lang against i18n.ValidLanguages in validateBindFlags and a new validateInitLang — empty string, wrong case, typos, and removed codes all exit 2 via output.ErrValidation
  • Collapse getBindMsg / getInitMsg to a bilingual switch and delete the 12 non-zh/en message structs (cmd/config/bind_messages.go, cmd/config/init_messages.go)
  • Shrink promptLangSelection to 2 options (中文 / English); the picker now writes both Lang and UILang
  • Print a language-preference confirmation to stderr only when --lang is explicit (LangPreferenceSet message field)
  • JSON envelope message field follows Lang (preference) for AI-agent consumption, while stderr TUI text follows UILang
  • Remove the now-unused i18n.NormalizeLang; trim ValidLanguages to 14 and update its count assertion
  • Update --lang help text to describe preference semantics instead of "interactive prompts"

Test Plan

  • make unit-test passed (per-package; the aggregate run is resource-constrained in the sandbox, every package passes in isolation — cmd/config, internal/i18n, plus 16/16 sequential ok)
  • validate passed (build / vet / integration; unit-test caveat above)
  • local-eval skipped: lite mode (E2E sandbox not run); skillave N/A (no shortcut/skill/meta changes)
  • acceptance-reviewer passed (2/2 scenarios — core happy-path + invalid-lang exit-2)
  • manual verification: config init --lang fr ... → exit 0, lang:"fr" on disk, zh confirmation 语言偏好已设置:fr; --lang frr|ZH|""|ar → exit 2 with structured validation error; config init --help lists exactly 14 codes

Related Issues

N/A

Summary by CodeRabbit

  • New Features
    • Persistent language preference via --lang and a separate per-run TUI display language (defaults to Chinese). Interactive picker sets session TUI language; machine-readable output uses the persisted preference. Confirmation shown on success only when --lang was explicitly provided.
  • Bug Fixes / Validation
    • Improved canonicalization and validation of language inputs with clearer error messages; omitting --lang preserves prior preference.
  • Tests
    • Expanded tests for validation, fallback behavior, picker interaction, and confirmation output.

Review Change Stack

Empty string (explicit), wrong case, typos, and removed language codes
all exit with ExitValidation (code 2). Empty Lang on bypass-cobra test
paths normalizes to default 'zh' to preserve test ergonomics while
keeping the user-facing contract strict.
After the --lang refactor, the only callers of NormalizeLang were the
picker pre-select (now hardcoded zh) and getBindMsg/getInitMsg default
branches (now bilingual collapse). Strict validation at the flag-parse
boundary makes silent normalization unnecessary.
The help text still said 'language for interactive prompts', which
contradicts the refactor — --lang no longer controls TUI language.
Updated to describe it as a downstream API/output preference that does
not change this command's TUI language. (acceptance-reviewer finding)
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Add typed i18n (internal/i18n), ParseLangFlag, persist AppConfig.Lang, and separate persisted Lang from per-run UILang; update config init/bind flows, message bundles, mail signature runtime, login/profile handling, and many tests to canonicalize/validate language flags.

Changes

Language Preference Separation & i18n rollout

Layer / File(s) Summary
i18n language type and helpers
internal/i18n/lang.go, internal/i18n/lang_test.go
Add Lang type, canonical locale catalog, Parse, IsEnglish, Base, and Codes with unit tests.
Core config & runtime plumbing
internal/core/config.go, shortcuts/common/runner.go, shortcuts/common/runner_lang_test.go
Persist AppConfig.Lang, expose CliConfig.Lang, and add RuntimeContext.Lang() to parse stored language for runtime consumers.
Shared flag parser
internal/cmdutil/lang.go
New ParseLangFlag canonicalizes non-empty --lang into i18n.Lang, treats empty as unset, and returns validation error with allowed codes on failure.
Config init options, validation, and persistence
cmd/config/init.go, cmd/config/config_test.go
Add UILang to ConfigInitOptions (default zh), validate/canonicalize --lang early, use preferredLang to preserve prior saved value when omitted, and print stderr confirmation only when --lang explicit. Tests added/updated for invalid inputs and confirmation printing.
Init messages & picker refactor
cmd/config/init_messages.go, cmd/config/init_messages_test.go
Add LangPreferenceSet message field; getInitMsg and promptLangSelection refactored to typed i18n.Lang; tests for bilingual collapse and non-en fallback updated.
Config bind options, TUI, and commit logic
cmd/config/bind.go, cmd/config/bind_test.go
BindOptions gains Lang, langExplicit, UILang (default zh); TUI uses UILang for prompts/brand rendering, picker sets both when --lang omitted; applyPreferences/preferredLang preserve prior persisted value; commitBinding prints conditional confirmation when --lang explicit; validateBindFlags uses ParseLangFlag.
Bind messages & tests
cmd/config/bind_messages.go, cmd/config/bind_messages.go, cmd/config/bind_test.go
Add LangPreferenceSet to bind messages (zh/en), switch getBindMsg/brandDisplay to i18n.Lang, and expand tests for invalid lang, non-en fallback, and explicit-lang stderr confirmation.
Auth, profile, and shortcuts
cmd/auth/*, cmd/profile/*, shortcuts/mail/*, shortcuts/mail/mail_signature_lang_test.go
Switch login & login message plumbing to typed i18n; profile add parses/canonicalizes --lang and stores i18n.Lang; mail signature maps runtime Lang to API locales; tests updated/added for those behaviors.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • larksuite/cli#728: overlaps cmd/config/bind.go / cmd/config/bind_messages.go changes around identity-risk warnings and localized message selection.

Suggested labels

size/XL, domain/ccm

Suggested reviewers

  • liangshuo-1
  • liuxinyanglxy
  • chanthuang

🐰 Two tongues now split, one deep, one bright—
Lang endures, while UILang guides the sight,
Picker defaults zh when no flag is shown,
Confirmation prints when choices are known.
Tests hop in to guard each bilingual line.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: decoupling the language preference flag from TUI display language.
Description check ✅ Passed The PR description is comprehensive, covering summary, detailed changes, and test plan aligned with the template structure.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cliconfig-lang-multilang

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@CLAassistant

CLAassistant commented May 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label May 27, 2026
@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.67669% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.70%. Comparing base (cdae999) to head (6595ffc).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
cmd/config/bind.go 72.09% 12 Missing ⚠️
cmd/config/init.go 66.66% 11 Missing ⚠️
internal/cmdutil/lang.go 0.00% 9 Missing ⚠️
cmd/config/init_messages.go 28.57% 5 Missing ⚠️
cmd/auth/login.go 50.00% 1 Missing ⚠️
cmd/profile/add.go 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1132      +/-   ##
==========================================
+ Coverage   68.31%   68.70%   +0.39%     
==========================================
  Files         619      627       +8     
  Lines       57431    58449    +1018     
==========================================
+ Hits        39232    40158     +926     
- Misses      14959    15002      +43     
- Partials     3240     3289      +49     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@6595ffc6a3d9645c99c630918c31b38e911629a1

🧩 Skill update

npx skills add larksuite/cli#feat/cliconfig-lang-multilang -y -g

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/config/init_messages_test.go (1)

50-67: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include LangPreferenceSet in the non-empty message field assertions.

The new initMsg.LangPreferenceSet field isn’t validated by assertAllFieldsNonEmpty, so an empty confirmation template would pass tests unnoticed.

Suggested patch
 	fields := map[string]string{
 		"SelectAction":         msg.SelectAction,
 		"CreateNewApp":         msg.CreateNewApp,
 		"ConfigExistingApp":    msg.ConfigExistingApp,
 		"Platform":             msg.Platform,
 		"SelectPlatform":       msg.SelectPlatform,
 		"Feishu":               msg.Feishu,
 		"ScanQRCode":           msg.ScanQRCode,
 		"ScanOrOpenLink":       msg.ScanOrOpenLink,
 		"WaitingForScan":       msg.WaitingForScan,
 		"OpenLinkNonTTY":       msg.OpenLinkNonTTY,
 		"WaitingForScanNonTTY": msg.WaitingForScanNonTTY,
 		"DetectedLarkTenant":   msg.DetectedLarkTenant,
 		"AppCreated":           msg.AppCreated,
 		"ConfigSaved":          msg.ConfigSaved,
+		"LangPreferenceSet":    msg.LangPreferenceSet,
 	}
🤖 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 `@cmd/config/init_messages_test.go` around lines 50 - 67, The assertion helper
assertAllFieldsNonEmpty is missing the new initMsg field LangPreferenceSet, so
add "LangPreferenceSet": msg.LangPreferenceSet to the fields map inside
assertAllFieldsNonEmpty to ensure the LangPreferenceSet template is validated as
non-empty during tests; locate the fields map in assertAllFieldsNonEmpty and
include the LangPreferenceSet entry alongside the other message keys.
🧹 Nitpick comments (2)
internal/i18n/lang_test.go (1)

8-28: ⚡ Quick win

Cover all declared valid language codes in the positive path.

At Line 13, the table only checks a subset of valid codes. Add an assertion that iterates ValidLanguages and verifies each returns true to prevent list/function drift.

Proposed test hardening
 func TestIsValidLang(t *testing.T) {
 	tests := []struct {
 		lang     string
 		expected bool
 	}{
 		{"zh", true},
 		{"en", true},
 		{"ja", true},
 		{"ko", true},
 		{"invalid", false},
 		{"", false},
 		{"ZH", false}, // case sensitive
 	}
 	for _, tt := range tests {
 		t.Run(tt.lang, func(t *testing.T) {
 			if got := IsValidLang(tt.lang); got != tt.expected {
 				t.Errorf("IsValidLang(%q) = %v, want %v", tt.lang, got, tt.expected)
 			}
 		})
 	}
+	for _, lang := range ValidLanguages {
+		if !IsValidLang(lang) {
+			t.Errorf("IsValidLang(%q) = false, want true", lang)
+		}
+	}
 }
🤖 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 `@internal/i18n/lang_test.go` around lines 8 - 28, The test only checks a
subset of valid language codes; update TestIsValidLang to also iterate over the
ValidLanguages slice and assert IsValidLang(code) returns true for each entry so
the test will catch any drift between ValidLanguages and IsValidLang. Locate
TestIsValidLang and add a subtest or loop that goes through ValidLanguages (the
symbol ValidLanguages) and calls IsValidLang for each value, failing the test if
any valid code returns false.
internal/core/config.go (1)

162-163: ⚡ Quick win

Clarify CliConfig.Lang as preference, not display language.

At Line 162, “UI language” can be misread as TUI render language. This PR separates preference (Lang) from display (UILang), so tightening this comment will reduce future misuse.

Suggested wording update
-	Lang                string // UI language (zh, en, ja, ko, etc.)
+	Lang                string // Persistent language preference (e.g. zh, en, ja, ko)
🤖 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 `@internal/core/config.go` around lines 162 - 163, Update the comment for the
CliConfig.Lang field to state that it is the user's language preference for
content/translation (e.g., "preferred language for prompts, responses, and
content") and not the terminal/UI render language; reference the separate UILang
field (e.g., "use UILang for TUI rendering/localization") so future readers
don't confuse CliConfig.Lang with display rendering. Ensure the comment mentions
CliConfig.Lang and UILang by name so maintainers can find them easily.
🤖 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 `@cmd/config/config_test.go`:
- Around line 186-188: In TestConfigInitCmd_InvalidLang add isolation for config
state by calling t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at the start
of the test (before clearAgentEnv and any test table iterations) so the test
uses a unique temporary config directory; update any related new table tests in
the same file to do the same to avoid cross-test interference.

In `@cmd/config/init.go`:
- Around line 338-344: The picker error handling in the init command currently
returns raw errors from promptLangSelection (only huh.ErrUserAborted is
wrapped), which can leak huh errors into RunE; replace the bare return err with
a structured output error (e.g., use output.Errorf or output.ErrWithHint) so all
non-abort failures are returned as output.* errors—keep the huh.ErrUserAborted
branch returning output.ErrBare(1) and for other errors wrap err using
output.Errorf("failed to select language: %v", err) or output.ErrWithHint as
appropriate; update the block around promptLangSelection to use these output.*
wrappers so RunE only receives structured output errors.

---

Outside diff comments:
In `@cmd/config/init_messages_test.go`:
- Around line 50-67: The assertion helper assertAllFieldsNonEmpty is missing the
new initMsg field LangPreferenceSet, so add "LangPreferenceSet":
msg.LangPreferenceSet to the fields map inside assertAllFieldsNonEmpty to ensure
the LangPreferenceSet template is validated as non-empty during tests; locate
the fields map in assertAllFieldsNonEmpty and include the LangPreferenceSet
entry alongside the other message keys.

---

Nitpick comments:
In `@internal/core/config.go`:
- Around line 162-163: Update the comment for the CliConfig.Lang field to state
that it is the user's language preference for content/translation (e.g.,
"preferred language for prompts, responses, and content") and not the
terminal/UI render language; reference the separate UILang field (e.g., "use
UILang for TUI rendering/localization") so future readers don't confuse
CliConfig.Lang with display rendering. Ensure the comment mentions
CliConfig.Lang and UILang by name so maintainers can find them easily.

In `@internal/i18n/lang_test.go`:
- Around line 8-28: The test only checks a subset of valid language codes;
update TestIsValidLang to also iterate over the ValidLanguages slice and assert
IsValidLang(code) returns true for each entry so the test will catch any drift
between ValidLanguages and IsValidLang. Locate TestIsValidLang and add a subtest
or loop that goes through ValidLanguages (the symbol ValidLanguages) and calls
IsValidLang for each value, failing the test if any valid code returns false.
🪄 Autofix (Beta)

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

Review profile: CHILL

Plan: Pro

Run ID: e71fe396-b371-4aa4-9366-d7ba958f06aa

📥 Commits

Reviewing files that changed from the base of the PR and between 70081f6 and 066543c.

📒 Files selected for processing (10)
  • cmd/config/bind.go
  • cmd/config/bind_messages.go
  • cmd/config/bind_test.go
  • cmd/config/config_test.go
  • cmd/config/init.go
  • cmd/config/init_messages.go
  • cmd/config/init_messages_test.go
  • internal/core/config.go
  • internal/i18n/lang.go
  • internal/i18n/lang_test.go

Comment thread cmd/config/config_test.go
Comment thread cmd/config/init.go
Bare picker errors leaked through RunE, breaking structured stderr JSON
for agent callers. Wrap non-abort huh failures in output.Errorf in both
bind and init. Also isolate the InvalidLang test config dir, assert the
new LangPreferenceSet field is non-empty, guard ValidLanguages drift,
and clarify the CliConfig.Lang comment as preference (not TUI language).

Addresses CodeRabbit review on PR #1132.
Add unit tests for the flag-mode language-preference confirmation line
(bind + init), the printLangPreferenceConfirmation helper (both explicit
and implicit branches), and validateInitLang's empty-implicit normalize
path. Raises patch coverage on the new --lang preference code; the
remaining uncovered lines are interactive picker/TUI paths.

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

♻️ Duplicate comments (1)
cmd/config/config_test.go (1)

460-468: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Isolate config state in the newly added tests.

Please set LARKSUITE_CLI_CONFIG_DIR to a temp dir at test start so these tests remain hermetic and cannot leak/read shared config state.

Suggested patch
 func TestValidateInitLang_NormalizesEmptyImplicitToZh(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	opts := &ConfigInitOptions{Lang: "", langExplicit: false}
 	if err := validateInitLang(opts); err != nil {
 		t.Fatalf("expected nil error, got %v", err)
 	}
@@
 func TestPrintLangPreferenceConfirmation(t *testing.T) {
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	t.Run("explicit prints confirmation", func(t *testing.T) {
 		f, _, stderr, _ := cmdutil.TestFactory(t, nil)
 		printLangPreferenceConfirmation(&ConfigInitOptions{Factory: f, Lang: "en", UILang: "zh", langExplicit: true})

As per coding guidelines, **/*_test.go: Use t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state in tests.

Also applies to: 472-488

🤖 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 `@cmd/config/config_test.go` around lines 460 - 468, The tests modify/inspect
config and must isolate config state: at the start of
TestValidateInitLang_NormalizesEmptyImplicitToZh and the other test covering
lines 472-488, call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) so each
test uses a fresh temp config directory; add this single line as the first
statement in those test functions (referencing the test function names
TestValidateInitLang_NormalizesEmptyImplicitToZh and the adjacent test) to
ensure hermetic behavior.
🤖 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.

Duplicate comments:
In `@cmd/config/config_test.go`:
- Around line 460-468: The tests modify/inspect config and must isolate config
state: at the start of TestValidateInitLang_NormalizesEmptyImplicitToZh and the
other test covering lines 472-488, call t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()) so each test uses a fresh temp config directory; add this single
line as the first statement in those test functions (referencing the test
function names TestValidateInitLang_NormalizesEmptyImplicitToZh and the adjacent
test) to ensure hermetic behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43a5d826-997b-4e01-9eea-2ca3f811b619

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3d42c and 1574074.

📒 Files selected for processing (2)
  • cmd/config/bind_test.go
  • cmd/config/config_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/config/bind_test.go

Set LARKSUITE_CLI_CONFIG_DIR to a temp dir in the two newly added tests
to follow the repo test-isolation convention (addresses CodeRabbit).
@luozhixiong01

Copy link
Copy Markdown
Collaborator Author

Reviewer summary

This PR decouples the --lang flag from the command's TUI display language, so --lang is now a pure, strictly-validated preference while the on-screen language is controlled separately.

What changed (behavior)

  • --lang = preference only. It sets the persisted Lang and the JSON envelope message (consumed by downstream AI agents). It no longer changes what language the interactive prompts render in.
  • TUI display language = UILang (new field, default zh). Only the interactive picker writes it; the picker is reduced to 2 options (中文 / English).
  • Strict validation. Invalid --lang (empty / wrong case / not in the 14-code i18n.ValidLanguages) now exits 2 with a structured validation error, instead of silently falling back.
  • Confirmation line. When --lang is passed explicitly, a "language preference set" line is printed to stderr.
  • The JSON envelope message intentionally follows Lang (preference), while the stderr banner follows UILang — different audiences (agent vs human).

Key design point

message (stdout JSON, for agents) → follows Lang. The stderr banner (for humans) → follows UILang. This split is the whole point of the refactor; message-follows-Lang is pre-existing behavior, preserved.

Verification

  • All CI green: unit-test, lint, security, CodeQL Analyze, e2e-live, license/cla.
  • codecov/patch 67.79% (target 60%); codecov/project 68.32% (+0.47%).
  • CodeRabbit review addressed: picker errors wrapped in output.Errorf (bind + init), test isolation, LangPreferenceSet non-empty assertion, ValidLanguages drift guard, Lang comment clarified.
  • Remaining uncovered patch lines are interactive picker / TUI form paths that unit tests can't exercise.

Ready for review — the only gate left is approval.

…& profile

- i18n.Lang string enum with a single-source catalog (Parse/IsEnglish/Base/Codes)
- --lang accepts a short code or Feishu locale (zh|zh_cn), stores canonical;
  empty is unset (no-op) and never clobbers an existing preference
- expose RuntimeContext.Lang() and collapse mail resolveLang onto it
- share cmdutil.ParseLangFlag across config bind/init and profile add

Change-Id: Iede1a375c3d962440ad3ccb6fd4161bad7d62f49
…ultilang

Change-Id: I70f918998ec8454ec6e80e14d6867d780d969561
…ract

- bind/init rebuild the config fresh, so an unset --lang dropped a previously
  stored preference; inherit it instead (regression tests added)
- express the empty-never-clobbers rule once via preferredLang, used by every
  bind/init write site
- i18n: Parse/Base/IsEnglish share one find() over the catalog

Change-Id: I094e470be0942195f36eb6af355dd8c15123ed18
@github-actions github-actions Bot added the domain/mail PR touches the mail domain label May 28, 2026

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

Actionable comments posted: 1

🤖 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 `@cmd/config/bind.go`:
- Around line 457-459: The stdout JSON message is being rendered with
i18n.Lang(opts.Lang) instead of the resolved language returned by
applyPreferences, so when --lang is unset but a prior config language exists the
wrong language is used; update the code that builds prefMsg and brand (currently
calling getBindMsg and brandDisplay with i18n.Lang(opts.Lang)) to use the
resolved language value produced by applyPreferences (use that resolvedLang
variable or field) when calling getBindMsg(...) and brandDisplay(...), ensuring
the envelope message matches the persisted/resolved language rather than
opts.Lang.
🪄 Autofix (Beta)

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

Review profile: CHILL

Plan: Pro

Run ID: d370ca0d-bd1f-4fc9-8531-c201a7c8aedf

📥 Commits

Reviewing files that changed from the base of the PR and between 0794bc9 and 6604b22.

📒 Files selected for processing (20)
  • cmd/auth/login.go
  • cmd/auth/login_messages.go
  • cmd/auth/login_messages_test.go
  • cmd/config/bind.go
  • cmd/config/bind_messages.go
  • cmd/config/bind_test.go
  • cmd/config/config_test.go
  • cmd/config/init.go
  • cmd/config/init_messages.go
  • cmd/config/init_messages_test.go
  • cmd/profile/add.go
  • cmd/profile/profile_test.go
  • internal/cmdutil/lang.go
  • internal/core/config.go
  • internal/i18n/lang.go
  • internal/i18n/lang_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/runner_lang_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/mail/mail_signature_lang_test.go
💤 Files with no reviewable changes (3)
  • shortcuts/mail/mail_signature_lang_test.go
  • shortcuts/mail/mail_signature.go
  • shortcuts/common/runner_lang_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/core/config.go
  • cmd/config/bind_test.go
  • cmd/config/init.go

Comment thread cmd/config/bind.go Outdated
The re-bind inheritance contract introduced in 6604b22 was incomplete:
appConfig.Lang inherited the prior preference, but commitBinding still
rendered the JSON envelope "message" via opts.Lang (raw --lang). An AI
agent that set --lang en on first bind would see Chinese envelope text
on every subsequent re-bind even though en_us stayed on disk.

Route prefMsg + brandDisplay through appConfig.Lang (the resolved
preference) instead. Regression test pins envelope.message to the
inherited language.

Change-Id: I8e3d2a1f4b9c5a6e7d8f9a0b1c2d3e4f5a6b7c8d
@liangshuo-1
liangshuo-1 merged commit 3b77055 into main May 28, 2026
19 checks passed
@liangshuo-1
liangshuo-1 deleted the feat/cliconfig-lang-multilang branch May 28, 2026 10:55
liangshuo-1 added a commit that referenced this pull request May 28, 2026
…ntApp in priorLang

Two issues caught in review of #1132 that the existing tests missed because
they constructed RuntimeContext/CliConfig directly, bypassing the credential
edge where the bug lives.

P1 — Lang dropped at credential boundary
  credential.Account had no Lang field, so AccountFromCliConfig and
  ToCliConfig silently dropped cfg.Lang. The production Factory builds
  CliConfig via acct.ToCliConfig() (factory_default.go Phase 3), which
  meant RuntimeContext.Lang() always returned "" in production and
  shortcuts/mail/mail_signature.go always fell back to zh_cn — defeating
  the whole point of persisting --lang.

  Fix: add Lang i18n.Lang to Account and copy it in both directions.

  Regression test: TestFullChain_LangSurvivesProductionPath walks the
  real path (SaveMultiAppConfig -> DefaultAccountProvider.ResolveAccount
  -> ToCliConfig) and asserts Lang survives, so any future field added
  to CliConfig forces the same audit.

P2 — priorLang ignored CurrentApp in multi-profile workspaces
  priorLang scanned all Apps and returned the first non-empty Lang. If a
  user had multiple profiles and the active one disagreed with Apps[0],
  a re-bind without --lang would silently inherit the wrong profile's
  preference.

  Fix: read multi.CurrentAppConfig("").Lang instead.

  Regression tests cover CurrentApp wins over Apps[0], single-app
  fallback, and malformed bytes.

Change-Id: If7a276605f84f398cec329c2c942b471b4c32749
liangshuo-1 added a commit that referenced this pull request May 28, 2026
…ntApp in priorLang (#1157)

Two issues caught in review of #1132 that the existing tests missed because
they constructed RuntimeContext/CliConfig directly, bypassing the credential
edge where the bug lives.

P1 — Lang dropped at credential boundary
  credential.Account had no Lang field, so AccountFromCliConfig and
  ToCliConfig silently dropped cfg.Lang. The production Factory builds
  CliConfig via acct.ToCliConfig() (factory_default.go Phase 3), which
  meant RuntimeContext.Lang() always returned "" in production and
  shortcuts/mail/mail_signature.go always fell back to zh_cn — defeating
  the whole point of persisting --lang.

  Fix: add Lang i18n.Lang to Account and copy it in both directions.

  Regression test: TestFullChain_LangSurvivesProductionPath walks the
  real path (SaveMultiAppConfig -> DefaultAccountProvider.ResolveAccount
  -> ToCliConfig) and asserts Lang survives, so any future field added
  to CliConfig forces the same audit.

P2 — priorLang ignored CurrentApp in multi-profile workspaces
  priorLang scanned all Apps and returned the first non-empty Lang. If a
  user had multiple profiles and the active one disagreed with Apps[0],
  a re-bind without --lang would silently inherit the wrong profile's
  preference.

  Fix: read multi.CurrentAppConfig("").Lang instead.

  Regression tests cover CurrentApp wins over Apps[0], single-app
  fallback, and malformed bytes.

Change-Id: If7a276605f84f398cec329c2c942b471b4c32749
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
…ntApp in priorLang (larksuite#1157)

Two issues caught in review of larksuite#1132 that the existing tests missed because
they constructed RuntimeContext/CliConfig directly, bypassing the credential
edge where the bug lives.

P1 — Lang dropped at credential boundary
  credential.Account had no Lang field, so AccountFromCliConfig and
  ToCliConfig silently dropped cfg.Lang. The production Factory builds
  CliConfig via acct.ToCliConfig() (factory_default.go Phase 3), which
  meant RuntimeContext.Lang() always returned "" in production and
  shortcuts/mail/mail_signature.go always fell back to zh_cn — defeating
  the whole point of persisting --lang.

  Fix: add Lang i18n.Lang to Account and copy it in both directions.

  Regression test: TestFullChain_LangSurvivesProductionPath walks the
  real path (SaveMultiAppConfig -> DefaultAccountProvider.ResolveAccount
  -> ToCliConfig) and asserts Lang survives, so any future field added
  to CliConfig forces the same audit.

P2 — priorLang ignored CurrentApp in multi-profile workspaces
  priorLang scanned all Apps and returned the first non-empty Lang. If a
  user had multiple profiles and the active one disagreed with Apps[0],
  a re-bind without --lang would silently inherit the wrong profile's
  preference.

  Fix: read multi.CurrentAppConfig("").Lang instead.

  Regression tests cover CurrentApp wins over Apps[0], single-app
  fallback, and malformed bytes.

Change-Id: If7a276605f84f398cec329c2c942b471b4c32749
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/mail PR touches the mail domain feature size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants