Skip to content

Translate remaining UI strings - #92

Merged
touch2be merged 5 commits into
masterfrom
fix/i18n-missing-translations
Jun 14, 2026
Merged

touch2be merged 5 commits into
masterfrom
fix/i18n-missing-translations

Conversation

@touch2be

Copy link
Copy Markdown
Collaborator

Summary

  • move remaining hardcoded UI copy into i18n keys
  • add English, German, and Spanish translations
  • document translation updates in AGENTS.md for future UI text changes

Checks

  • vp check (passes; existing pi-bridge.ts await-thenable warnings remain)

@github-actions

Copy link
Copy Markdown

Confidence Score: 4


Issues

1. Hardcoded "Merge" button in src/App.tsx:225

The Merge button next to "Create PR" uses a raw string instead of t():

<GitMerge className="size-4" />
Merge

The key projectMenu.merge already exists in all locale files. Should be {t("projectMenu.merge")}.

2. Multiple untranslated strings in src/components/ProjectPathDialog.tsx

Several strings remain hardcoded even though useTranslation was added:

  • getPromptMessage() function (lines 42-47) — returns hardcoded English:
function getPromptMessage(isLocalWorkspace: boolean) {
  if (isLocalWorkspace) {
    return "Open a local project folder for this window. You can also paste an absolute path manually.";
  }
  return "This window is connected to a remote server, so choose a project by entering the path on that server.";
}
  • Dialog title (line 158): title="Open Project"
  • Description (lines 160-162): conditional ? "Choose a project path on the OpenGUI server..." / getPromptMessage(...)
  • "Cancel" button (line 167): should use t("common.cancel")
  • "Open project" button (line 170): should use a new key
  • "Project path" label (line 183): label="Project path"
  • Browse button (line 214): {webRuntime || !isLocalWorkspace ? "Browse server" : "Browse"}
  • Loading text (line 221): {serverListing?.path ?? "Loading server folders..."}

These strings also need corresponding keys added to all three locale files.

3. Hidden refactor in MergeDialog.tsx (lines 30-209)

The component was substantially rewritten from a multi-modal pattern to a single state-machine pattern. This is a net improvement but the scope is larger than the PR title suggests. The intermediate return statements for each mergeState.step break the React hooks rules — the useTranslation() and useOpenGuiClient() hooks are fine since they're called before any early return, but this pattern warrants extra review attention.

New%20session%20-%202026-06-14T01%3A06%3A15.542Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

Confidence Score: 4


Issues

1. Hardcoded "Merge" button in src/App.tsx:225

The Merge button next to "Create PR" uses a raw string instead of t():

<GitMerge className="size-4" />
Merge

The key projectMenu.merge already exists in all three locale files. Should be {t("projectMenu.merge")}. This was added to the file's scope (t is now available via useTranslation at line 50) but not applied here.


2. Multiple untranslated strings in src/components/ProjectPathDialog.tsx

Several strings remain hardcoded even though useTranslation was added:

  • getPromptMessage() function (lines 42-47): returns hardcoded English with no i18n keys
  • Dialog title (line 158): title="Open Project"
  • Description (lines 159-162): conditional ? "Choose a project path on the OpenGUI server..." / getPromptMessage(isLocalWorkspace)
  • "Cancel" button (line 167): should use t("common.cancel")
  • "Open project" button (line 170): needs a new i18n key (or use an existing one)
  • "Project path" label (line 183): label="Project path"
  • Browse button (line 214): {webRuntime || !isLocalWorkspace ? "Browse server" : "Browse"}
  • Loading text (line 221): {serverListing?.path ?? "Loading server folders..."}

These strings also need corresponding keys in en.json, de.json, and es.json.


3. Hardcoded strings in RevertBanner (src/components/MessageList.tsx:79-106)

The RevertBanner component is defined inside MessageList.tsx but does not use t():

{revertedCount} message{revertedCount !== 1 ? "s" : ""} reverted
Restore

4. No guard against double-merge execution (src/components/MergeDialog.tsx:57-78)

const handleMerge = useCallback(async () => {
  setMergeState({ step: "merging" });
  try {
    const res = await client.git.merge(mainDirectory, branch);

If the user clicks "Merge" rapidly, handleMerge can be invoked multiple times, each starting a concurrent merge. There is no check at the top (e.g. if (mergeState.step === "merging") return) to prevent re-entry. The button is disabled implicitly during the "merging" step because of the early return at line 92, but the callback closure captures mergeState from when the button was rendered, so a stale closure could bypass this. A useRef guard or an early return guard is advisable.


5. MergeDialog refactor scope (src/components/MergeDialog.tsx)

The component was substantially rewritten from individual useState/useEffect flags to a discriminated union state machine. While this is a net improvement, the scope is larger than the PR title suggests. The early return pattern (lines 92, 105, 125, 163, 182) is valid since all hooks are called before any conditional return, but it is unconventional and could be fragile — any future addition of hook calls after these returns would silently break the Rules of Hooks.

New%20session%20-%202026-06-14T01%3A19%3A53.750Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

Confidence Score: 5


Issues Detected

The PR's initial commit (dc986b0) had 10 issues, all fixed by the follow-up (ce7333b). The current HEAD is clean. Below are the issues that existed and were self-corrected during the PR:

1. No guard against double-merge execution (CRITICAL — fixed)

MergeDialog.tsx:57 originally had no re-entry guard. Rapid clicks on "Merge" would fire concurrent client.git.merge() calls. Fixed by adding mergeInFlightRef with guard check and finally block.

2. No finally block on merge (MODERATE — fixed)

The try/catch lacked finally, so mergeInFlightRef would stay true permanently after any outcome.

3. RevertBanner fully untranslated — 2 hardcoded strings (MODERATE — fixed)

MessageList.tsx — {revertedCount} message{revertedCount !== 1 ? "s" : ""} reverted and Restore were raw strings. No useTranslation import existed.

4. Permission panel untranslated — 4 hardcoded strings (MODERATE — fixed)

MessageList.tsx — permission panel strings like tooltips and headers were not translated.

5. ProjectPathDialog.tsx — 8+ hardcoded strings (MAJOR — fixed)

getPromptMessage() returned raw English, title="Open Project", label="Project path", "Browse server"/"Browse", "Loading server folders...", "Cancel", "Open project" all hardcoded despite useTranslation being added.

6. Locale files missing keys (MAJOR — fixed)

en.json, de.json, es.json were missing projectPath.* (8 keys), revertBanner.* (3 keys), and permissionPanel.* (5 keys) that the code referenced, causing raw key names to render.


Remaining Minor Observation (not a bug)

  • MergeDialog.tsx:86 — handleAbort doesn't check mergeInFlightRef. Safe today (only reachable in conflicts state), but could silently break if the abort button is ever exposed during the "merging" step. Consider adding the guard proactively.

New%20session%20-%202026-06-14T01%3A25%3A54.317Z
opencode session  |  github run

@touch2be
touch2be merged commit ee44bd9 into master Jun 14, 2026
1 check passed
@github-actions

Copy link
Copy Markdown

Confidence Score: 3


Issues Detected

1. CRITICAL: 90+ provider SVG icons added — unrelated to PR title (scope creep)

Commit 51638b2 adds 90+ SVG icon files (src/components/provider-icons/svgs/) and removes them from .gitignore. The PR title says "Translate remaining UI strings" — these icons have nothing to do with i18n. The PR body also makes no mention of this change.

.gitignore                                         |  4 ----
src/components/provider-icons/svgs/*.svg            | 90+ files added

These ~464 lines should be in a separate PR or the title/description must be updated to reflect them.

2. questionPanel locale keys added but unreferenced anywhere in source code

All three locale files (en.json:370-374, de.json:370-374, es.json:370-374) include:

"questionPanel": {
  "title": "The assistant has a question",
  "customAnswerPlaceholder": "Type a custom answer...",
  "submit": "Submit",
  "dismiss": "Dismiss"
}

No .tsx or .ts file in the codebase references questionPanel.*. These are dead keys that add unnecessary maintenance burden.

3. Initial commit (dc986b0) was incomplete — missed many strings in ProjectPathDialog.tsx

The initial commit added useTranslation and t() to ProjectPathDialog.tsx but failed to translate:

  • title="Open Project"
  • Description text (hardcoded English branches)
  • "Cancel" button
  • "Open project" button
  • label="Project path"
  • "Browse server" / "Browse" strings
  • "Loading server folders..." text

These were only caught and fixed in the follow-up commit ce7333b, indicating the initial pass was incomplete.

4. Unrelated bugfix in ModelSelector.tsx

finalFocus was replaced with onCloseAutoFocus — a Radix Dialog behavior fix unrelated to translation. This should have been a separate change.

-        finalFocus={() =>
-          document.querySelector<HTMLTextAreaElement>('[data-slot="prompt-box-textarea"]')
-        }
+        onCloseAutoFocus={(e) => {
+          e.preventDefault();
+          document.querySelector<HTMLTextAreaElement>('[data-slot="prompt-box-textarea"]')?.focus();
+        }}

5. MergeDialog refactor exceeds PR scope

MergeDialog.tsx was substantially rewritten: multi-useState flags → discriminated union state machine, added mergeInFlightRef guard with finally block. While these are net improvements, they go beyond "translate strings." The scope increase should be acknowledged in the PR description.

6. Fragile early-return pattern in MergeDialog

MergeDialog renders state-specific content via early returns after all hooks. Valid today since hooks precede all returns, but any future addition of a hook call after these returns will silently break the Rules of Hooks (src/components/MergeDialog.tsx:92,105,125,163,182).

7. handleAbort has no re-entry guard

handleAbort (line 86) doesn't check mergeInFlightRef. Safe today (only reachable during conflicts state), but looks inconsistent with handleMerge and could fail silently if the abort button is ever exposed during the merging step.


Despite these issues, the core translation work is solid: all keys are properly added to all three locales, i18next plural forms are correctly used for revertBanner.reverted, missing restartLocalBackend.* keys (already referenced in GeneralSettings.tsx) are properly populated, and AGENTS.md is updated. The PR just needs to be scoped more honestly.

New%20session%20-%202026-06-14T01%3A29%3A50.145Z
opencode session  |  github run

@akemmanuel
akemmanuel deleted the fix/i18n-missing-translations branch October 2, 2026 14:26
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.

1 participant