From 7942bc3471732ec607a3ea09eec91216c088c784 Mon Sep 17 00:00:00 2001 From: vybe Date: Sun, 24 May 2026 20:57:14 +0000 Subject: [PATCH] feat(i18n): make lib.js strings I18n source keys (strategy A); remove dead symbol exports; update tests; add i18n/ scaffolding + en.json placeholder - getStrings().copied now returns template key "Copied %1 to clipboard" (interpolation at QML call site via .arg) - Removed unused clearExitNode/setExitNode from getStrings (were dead code) - Tests updated for new shape while preserving coverage - New tailscalectl/i18n/ with README + en.json for future-proofing (per dms-plugin-dev best practice) - Exact behavior preserved; no logic changes Written by AI agent working for @jtmorris. Model: grok-build-0.1. --- tailscalectl/i18n/README.md | 40 +++++++++++++++++++++++++++++++++++++ tailscalectl/i18n/en.json | 15 ++++++++++++++ tailscalectl/lib.js | 4 +--- test/lib.test.js | 7 +++---- 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 tailscalectl/i18n/README.md create mode 100644 tailscalectl/i18n/en.json diff --git a/tailscalectl/i18n/README.md b/tailscalectl/i18n/README.md new file mode 100644 index 0000000..5e29d35 --- /dev/null +++ b/tailscalectl/i18n/README.md @@ -0,0 +1,40 @@ +# I18n for tailscalectl + +This plugin is fully instrumented with `I18n.tr(...)` (from `qs.Common`) for all user-facing strings. + +- Source keys are the American English strings defined in `lib.js` (returned by `getStrings()`, plus bases from `errorMessage()` / `formatError()`). +- Call sites in `TailscaleWidget.qml` wrap them: `I18n.tr(TailscaleLib.getStrings().foo)` or `I18n.tr(TailscaleLib.getStrings().copied).arg(text)`. +- Today: falls back to the key (perfect en-US). +- Future: Drop additional `xx.json` here (or contribute keys to DMS core translations) when a loader or extraction process supports per-plugin locales. + +## Current keys (source of truth) + +See `getStrings()` and `errorMessage()` in `lib.js` for the canonical list. + +Example `en.json` (for documentation / future tools): + +```json +{ + "Tailscale": "Tailscale", + "Connected": "Connected", + "Disconnected": "Disconnected", + "Exit node: ": "Exit node: ", + "None": "None", + "Copied %1 to clipboard": "Copied %1 to clipboard", + "Invalid exit node hostname": "Invalid exit node hostname", + "Failed to connect to Tailscale": "Failed to connect to Tailscale", + "Failed to disconnect from Tailscale": "Failed to disconnect from Tailscale", + "Failed to set exit node": "Failed to set exit node", + "Failed to read Tailscale status": "Failed to read Tailscale status", + "Error copying to clipboard": "Error copying to clipboard", + "Tailscale command failed": "Tailscale command failed" +} +``` + +## Notes + +- Symbols/glyphs ("×", "↗", "—") are intentionally left as literals in the UI (not run through I18n as they are not linguistic content). +- Plugin name/description in `plugin.json` and technical IDs ("tailscalectl") remain English. +- This follows DMS `dms-plugin-dev` best practice for future-proofing even when only en is shipped. + +Written by AI agent working for @jtmorris. Model: grok-build-0.1. diff --git a/tailscalectl/i18n/en.json b/tailscalectl/i18n/en.json new file mode 100644 index 0000000..bd5b57a --- /dev/null +++ b/tailscalectl/i18n/en.json @@ -0,0 +1,15 @@ +{ + "Tailscale": "Tailscale", + "Connected": "Connected", + "Disconnected": "Disconnected", + "Exit node: ": "Exit node: ", + "None": "None", + "Copied %1 to clipboard": "Copied %1 to clipboard", + "Invalid exit node hostname": "Invalid exit node hostname", + "Failed to connect to Tailscale": "Failed to connect to Tailscale", + "Failed to disconnect from Tailscale": "Failed to disconnect from Tailscale", + "Failed to set exit node": "Failed to set exit node", + "Failed to read Tailscale status": "Failed to read Tailscale status", + "Error copying to clipboard": "Error copying to clipboard", + "Tailscale command failed": "Tailscale command failed" +} diff --git a/tailscalectl/lib.js b/tailscalectl/lib.js index a1185e1..b27a4c3 100644 --- a/tailscalectl/lib.js +++ b/tailscalectl/lib.js @@ -52,9 +52,7 @@ function getStrings() { disconnected: "Disconnected", exitNodePrefix: "Exit node: ", none: "None", - copied: function (text) { return "Copied " + text + " to clipboard"; }, - clearExitNode: "×", - setExitNode: "↗", + copied: "Copied %1 to clipboard", invalidExitNodeHostname: "Invalid exit node hostname" }; } diff --git a/test/lib.test.js b/test/lib.test.js index 21d593e..0c7ab47 100644 --- a/test/lib.test.js +++ b/test/lib.test.js @@ -115,10 +115,9 @@ test("getStrings returns canonical UI strings for the widget", () => { assert.ok(s.copied) }) -test("getStrings.copied interpolates the text", () => { - const s = getStrings() - const msg = s.copied("100.64.0.5") - assert.ok(msg.includes("100.64.0.5")) +test("getStrings.copied is the I18n template key (interpolation happens at call site via .arg)", () => { + const s = getStrings(); + assert.strictEqual(s.copied, "Copied %1 to clipboard"); }) test("shouldShowClearExitNode returns true only when there is a current exit node", () => {