Skip to content

fix(v2): set LANG for pbpaste/pbcopy to prevent clipboard mojibake on macOS#5012

Merged
leaanthony merged 6 commits into
wailsapp:masterfrom
veeceey:fix/issue-4132-clipboard-encoding
Mar 17, 2026
Merged

fix(v2): set LANG for pbpaste/pbcopy to prevent clipboard mojibake on macOS#5012
leaanthony merged 6 commits into
wailsapp:masterfrom
veeceey:fix/issue-4132-clipboard-encoding

Conversation

@veeceey

@veeceey veeceey commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #4132

When a Wails app runs as a standalone macOS app (not launched from a terminal), the LANG environment variable is unset. pbpaste then defaults to an ASCII-compatible encoding, which turns non-ASCII clipboard content into garbage.

The fix ensures LANG=en_US.UTF-8 is set in the command's environment before invoking pbpaste/pbcopy, but only when LANG isn't already present — so it won't override anything the user has explicitly set.

Quick terminal demo showing the difference:

$ echo "你好世界 こんにちは" | pbcopy

$ LANG= pbpaste
Test: ???? ?????

$ LANG=en_US.UTF-8 pbpaste
Test: 你好世界 こんにちは

Summary by CodeRabbit

  • Bug Fixes
    • Improved macOS clipboard handling so copied and pasted text preserve UTF‑8 characters and no longer appear garbled in packaged apps.
  • Documentation
    • Added a changelog entry noting the macOS clipboard mojibake fix.

When macOS apps are launched as standalone (not from terminal), the LANG
environment variable is unset. This causes pbpaste to default to an
ASCII-compatible encoding, resulting in mojibake for non-ASCII clipboard
content.

Fix by ensuring LANG=en_US.UTF-8 is set in the command environment when
it's not already present.

Fixes wailsapp#4132
@veeceey

veeceey commented Feb 23, 2026

Copy link
Copy Markdown
Contributor Author

Manual test results (macOS 15, M4)

Verified the fix works by testing pbpaste behavior with and without LANG:

$ echo "你好世界 こんにちは" | pbcopy

# Without LANG (simulates standalone app launch):
$ LANG= pbpaste
Test: ???? ?????

# With LANG=en_US.UTF-8 (what this PR does):
$ LANG=en_US.UTF-8 pbpaste
Test: 你好世界 こんにちは

Also confirmed that go build ./v2/internal/frontend/desktop/darwin/ compiles cleanly.

@coderabbitai

coderabbitai Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5feb2fe0-28ed-4416-bb8e-409eff766d80

📥 Commits

Reviewing files that changed from the base of the PR and between cf15fd6 and cd70fbb.

📒 Files selected for processing (1)
  • website/src/pages/changelog.mdx

📝 Walkthrough

Walkthrough

Adds an ensureUTF8Env helper and applies it to the pbpaste and pbcopy command environments so macOS clipboard reads/writes run with LANG set to en_US.UTF-8 when LANG is unset.

Changes

Cohort / File(s) Summary
Clipboard UTF-8 Environment Fix
v2/internal/frontend/desktop/darwin/clipboard.go
Adds ensureUTF8Env() helper, imports os, and sets Cmd.Env = ensureUTF8Env(os.Environ()) for both ClipboardGetText (pbpaste) and ClipboardSetText (pbcopy). Adds explanatory comment about packaged macOS apps and LANG.
Changelog entry
website/src/pages/changelog.mdx
Adds a changelog line under "Fixed" describing the macOS clipboard mojibake fix and referencing the PR.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

Suggested reviewers

  • makew0rld

Poem

🐇 I nibble at bugs beneath the moonlight,
LANG set to UTF-8, clipboard feels right.
Pastes no longer garble, copies hum bright,
A tiny rabbit hop fixed text’s flight. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: setting LANG environment variable for pbpaste/pbcopy to prevent mojibake on macOS.
Description check ✅ Passed The description includes issue reference, problem explanation, solution details, and terminal demonstration. However, the Type of change checkbox and testing configuration sections required by the template are not completed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
v2/internal/frontend/desktop/darwin/clipboard.go (1)

14-22: Consider os.LookupEnv for a cleaner, more idiomatic implementation.

os.LookupEnv explicitly distinguishes "not set" from "set to empty" without manual string slicing, and is the standard Go idiom for this pattern:

♻️ Proposed refactor
 func ensureUTF8Env() []string {
 	env := os.Environ()
-	for _, e := range env {
-		if len(e) > 5 && e[:5] == "LANG=" {
-			return env
-		}
+	if _, ok := os.LookupEnv("LANG"); !ok {
+		env = append(env, "LANG=en_US.UTF-8")
 	}
-	return append(env, "LANG=en_US.UTF-8")
+	return env
 }

This also correctly handles the case where LANG is set but empty (keeping the existing empty value rather than creating a duplicate entry), which the current loop misses due to the > 5 boundary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v2/internal/frontend/desktop/darwin/clipboard.go` around lines 14 - 22,
Replace the manual loop in ensureUTF8Env with os.LookupEnv: use
os.LookupEnv("LANG") to detect whether LANG is present (allowing an existing
empty value to be preserved) and, if not present, append "LANG=en_US.UTF-8" to
os.Environ(); ensure you return the original env unchanged when LANG is set
(including empty string) to avoid duplicating entries and to follow Go idioms.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@v2/internal/frontend/desktop/darwin/clipboard.go`:
- Line 17: The guard that checks environment entries uses len(e) > 5 && e[:5] ==
"LANG=" which misses the exact "LANG=" entry; update the check in the loop over
environment entries (the variable e) to use a correct boundary such as len(e) >=
5 && e[:5] == "LANG=" or, better, strings.HasPrefix(e, "LANG=") so entries equal
to "LANG=" are treated as matches and the function will early-return instead of
appending a second LANG variable.

---

Nitpick comments:
In `@v2/internal/frontend/desktop/darwin/clipboard.go`:
- Around line 14-22: Replace the manual loop in ensureUTF8Env with os.LookupEnv:
use os.LookupEnv("LANG") to detect whether LANG is present (allowing an existing
empty value to be preserved) and, if not present, append "LANG=en_US.UTF-8" to
os.Environ(); ensure you return the original env unchanged when LANG is set
(including empty string) to avoid duplicating entries and to follow Go idioms.

Comment thread v2/internal/frontend/desktop/darwin/clipboard.go Outdated
Replace manual string slicing with os.LookupEnv which is more idiomatic
and correctly handles the edge case where LANG is set to an empty string.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@v2/internal/frontend/desktop/darwin/clipboard.go`:
- Around line 14-20: The function ensureUTF8Env correctly uses
os.LookupEnv("LANG") to detect presence (including an empty LANG=) and only
appends "LANG=en_US.UTF-8" when absent, so no code change is required; keep the
current implementation (ensureUTF8Env) as-is and approve the change.

@leaanthony

Copy link
Copy Markdown
Member

Thanks 🙏 Please could you add an entry to the changelog located at website/src/pages/changelog.mdx?

@veeceey

veeceey commented Feb 28, 2026

Copy link
Copy Markdown
Contributor Author

Added changelog entry. Thanks for the review!

@veeceey

veeceey commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

hey, any updates on this? the clipboard mojibake on macOS is a pretty annoying issue for non-ASCII users

@leaanthony leaanthony merged commit 0ce8919 into wailsapp:master Mar 17, 2026
1 of 4 checks passed
@leaanthony

leaanthony commented Mar 17, 2026

Copy link
Copy Markdown
Member

Thanks for your patience! And thanks again for creating this PR🙏

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.

Incorrect Clipboard Text Encoding when LANG Environment Variable is Empty on macOS

2 participants