@samitouri / QOSAMI-WSL / commits / 5c459b16

Document GCS Azure DevOps localization bug-tracking process (#40668)

* Document GCS Azure DevOps localization bug-tracking process Community localization PRs (Resources.resw edits and new locale files) are reviewed/incorporated by the Microsoft GCS localization pipeline via Azure DevOps Bug work items, not merged directly from GitHub. This adds a guide covering the ADO project coordinates, the Bug work-item template, required custom fields with known-good values, the Custom.Language picklist mapping, the az boards workflow, and the az.cmd/permission gotchas. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc-bug doc: correct working area path and required fields Area-path-scoped permissions: the working path is 'C and AI\Unspecified', not the project root. Also note System.Description is required alongside ReproSteps, and add the tr-TR language mapping. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc-bug doc: use Global\Windows area path (the durable one) Bugs filed under 'C and AI\Unspecified' get cleaned up and that access is not stable. The correct, durable path for WSL localization bugs is 'Global\Windows', confirmed against existing manually-filed WSL loc bugs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc-bug doc: add ACL-vs-existence, read-back, and encoding gotchas Capture the hard-won lessons from filing these bugs: - 'not found' from WIQL/GET often means no read access, not deletion; a gap in sequential IDs means items were created but are now invisible - always read the bug back after creating to confirm it persisted - set UTF-8 console encoding for non-ASCII locale content, and don't pipe az output straight into ConvertFrom-Json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc: clarify that translation fixes go through issues, not merged PRs Localized Resources.resw files (everything except en-US) are generated by the third-party GCS localization team and regenerated downstream, so any translation edits made in a GitHub PR get overwritten and cannot be merged. - CONTRIBUTING.md: tell contributors to file an issue for translation problems instead of opening a PR. - copilot-instructions.md + localization-bugs.md: when triaging, look for BOTH issues and PRs and file a tracking Bug for each; decline loc PRs with a pointer to the contributing guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc: cover all localized files, not just Resources.resw Localized content lives in two families, both generated by the GCS loc team and regenerated downstream: per-locale UI strings (localization/strings/<locale>/Resources.resw) and per-locale Intune/ group-policy templates (intune/<locale>/WSL.adml). Only the en-US sources and the language-neutral intune/WSL.admx originate in this repo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc-bug doc: require explicit resource-ID lists per GCS feedback GCS (Diarmuid O'Dowd) rejects abstract loc bugs that only name the file, language, and a few quoted strings. Document the new expectation: every bug must carry an explicit list of the resource IDs to fix (gold-standard Bug 1223866), add a 'Deriving the resource-ID list' section covering each report type (PR diff, line-number mapping, quoted-string grep, new-locale parity), update the description template, and note the More Info -> Active loop and the don't-inline-huge-diffs comment guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc: document post-filing automated pipeline; drop ADO-ID cross-link - Remove guidance to paste internal ADO Bug IDs into public GitHub PRs/issues; keep only the GitHub link inside the ADO bug. - Add 'What happens after you file' section covering the FabricClient-Prod DTT auto-processing, new-locale TDBuild onboarding via the Global Services Hub, and the oversized-report threshold failure. - Fix az boards work-item create example: drop redundant System.Description from --fields (already set via --description). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * loc docs: address PR review feedback - Reformat the resource-ID example as plain 'current/proposed' text instead of invalid multi-<value> .resw XML. - Describe the localization team as 'separate' rather than 'third-party' to match CONTRIBUTING.md and the GCS framing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Ben Hillis committed Jun 10, 2026 at 13:08 UTC 5c459b16953fcbb5bbb8979657b89c4f68d96613
3 files changed +245
.github/copilot-instructions.md
+2
@@ -104,6 +104,8 @@ Some older files use the block format (`/*++ Copyright (c) Microsoft. All rights
104 - Use `EMIT_USER_WARNING(Localization::MessageXxx(...))` for non-fatal config warnings
105 - All new user-facing strings must have entries in `localization/strings/en-US/Resources.resw`
106 - In Resources.resw comments, use `{Locked="..."}` to prevent translation of `.wslconfig` property key names
107 +- Localized files are generated by a separate localization team and regenerated downstream, so translation edits in a GitHub PR cannot be merged (they would be overwritten). This covers the per-locale `localization/strings/<locale>/Resources.resw` UI strings and the per-locale `intune/<locale>/WSL.adml` policy templates (anything other than the `en-US` sources and the neutral `intune/WSL.admx`). Filing a GitHub issue is the correct path for contributors; see `CONTRIBUTING.md`.
108 +- Community localization reports come in as **both GitHub issues and pull requests** - when triaging, look for both and file a tracking Bug for each. These are tracked via Bugs in the GCS Azure DevOps project, not merged directly. See `.github/copilot/localization-bugs.md` for the project coordinates, required fields, and `az boards` workflow.
109
110 ### Telemetry and Logging
111
.github/copilot/localization-bugs.md new
+237
@@ -0,0 +1,237 @@
1 +## Creating Localization Tracking Bugs (GCS Azure DevOps)
2 +
3 +Community members report localization problems in two ways: by filing a GitHub **issue** about a bad
4 +translation, or by opening a **pull request** that edits a localized file (the per-locale UI strings in
5 +`localization/strings/<locale>/Resources.resw`, or the per-locale Intune/group-policy templates in
6 +`intune/<locale>/WSL.adml`), or that adds a brand-new locale file. Both are valid signals and you should
7 +look for **both** when triaging.
8 +
9 +Either way, the fix is handled by the Microsoft Global Collaboration Service (GCS) localization pipeline,
10 +**not** merged directly from GitHub: every non-`en-US` localized file is generated by the GCS team and
11 +regenerated downstream, so any translation edit merged here would be overwritten. (PRs that touch
12 +localized files should be declined with a pointer to `CONTRIBUTING.md`; only the `en-US` sources and the
13 +language-neutral `intune/WSL.admx` live in this repo.) To route a report for review, file a tracking
14 +**Bug** in the GCS Azure DevOps project for each issue or PR.
15 +
16 +> **GCS requirement — be specific: list the exact resource IDs to fix.** The loc team will **not**
17 +> triage abstract reports. A bug that only names the file, language, and a few quoted strings gets
18 +> rejected (moved to the **More Info** state) because the team does not have capacity to parse a vague
19 +> report into the concrete strings that need work — that translation has to be done up front by the
20 +> filer. Every bug must therefore carry an explicit list of the **resource IDs** (the `name` attribute
21 +> of the `<data name="...">` elements in `localization/strings/<locale>/Resources.resw`) that need
22 +> fixing. The gold-standard level of detail is **Bug 1223866** (`.../GlobalCollaborationService/Global%20Collaboration%20Service%20Project/_workitems/edit/1223866`):
23 +> for each resource ID it gives the current (bad) value and the proposed value, e.g.
24 +>
25 +> ```text
26 +> MessageDistroAlreadySet
27 +> current: Der Verteilungsname wurde bereits festgelegt.
28 +> proposed: Der Distributionsname wurde bereits festgelegt.
29 +> ```
30 +
31 +### Deriving the resource-ID list
32 +
33 +How you obtain the IDs depends on the report type:
34 +
35 +- **PR editing an existing locale file** — the changed resource IDs are exactly the `<data name>`
36 + blocks whose `<value>` differs between the repo's current file and the PR head. Load both and diff
37 + by name. The PR "Files changed" view already shows current→proposed for each, so post the **compact
38 + ID list plus a link to the PR diff** rather than re-inlining every value (a 160-row inline table is
39 + unwieldy; the PR diff is the source of truth for the old/new text).
40 +
41 + ```powershell
42 + # changed resource IDs between two Resources.resw files (master vs PR head)
43 + function Get-ChangedIds($baseResw, $headResw) {
44 + function Load($p) { $x = [xml](Get-Content $p -Raw); $h = @{}; foreach ($d in $x.root.data) { $h[$d.name] = $d.value }; $h }
45 + $b = Load $baseResw; $h = Load $headResw
46 + $h.Keys | Where-Object { $b[$_] -ne $h[$_] }
47 + }
48 + ```
49 +
50 +- **Issue citing line numbers** (e.g. `Resources.resw#L185`) — fetch the file **at the exact commit
51 + the issue links to**, then walk upward from each cited line to the nearest `data name="..."` to get
52 + the enclosing resource ID. Line numbers drift between commits, so always pin to the linked SHA.
53 +
54 +- **Issue quoting bad strings** — grep the locale `Resources.resw` for each quoted value to find the
55 + `<data name>` that contains it. One displayed term often maps to several IDs (`.Header`,
56 + `.Description`, `...TextBox.AutomationProperties.Name`/`.HelpText`, shell-nav `.Content`), so list
57 + them all.
58 +
59 +- **PR adding a brand-new locale** — this is a net-new whole-file contribution, **not** a correction
60 + list, so there is nothing pre-existing to "fix". Give the team the actionable file-level items
61 + instead: (1) hard parity failures vs `en-US` that block `tools/devops/validate-localization.py` —
62 + placeholder/insert-count mismatches and missing `{Locked="..."}` tokens, listed by resource ID;
63 + (2) the coverage gap (which `en-US` IDs are missing, and how many of N total); (3) any strings
64 + present in the file but absent from `en-US` (obsolete — drop them); and (4) that a new locale needs
65 + build registration, not just a file drop.
66 +
67 +### Coordinates
68 +
69 +| | |
70 +|---|---|
71 +| Org | `https://dev.azure.com/GlobalCollaborationService` |
72 +| Project | `Global Collaboration Service Project` |
73 +| Process | `GCS_Agile` |
74 +| Work item type | `Bug` |
75 +| Web "create from template" link | `https://dev.azure.com/GlobalCollaborationService/Global%20Collaboration%20Service%20Project/_workitems/create/Bug?templateId=b97df4d3-4106-4099-a3a9-1782b5891bec` |
76 +
77 +### Required fields (and known-good values for a community loc PR)
78 +
79 +The Bug type has several required custom picklist fields. Values that work for "community translation PR
80 +needs review":
81 +
82 +| Field (reference name) | Value |
83 +|---|---|
84 +| `System.Title` | `WSL: Review community <locale> localization report (GitHub #<issue or PR>)` |
85 +| `System.Description` / `Microsoft.VSTS.TCM.ReproSteps` | GitHub link + scope + action (see below) |
86 +| `Custom.IssueType` | `Incorrect Translation` |
87 +| `Custom.ProductArea` | `Software` |
88 +| `Custom.How_Found` | `Community` |
89 +| `Custom.Severity_Impact` | `S3 - Medium` (field default) |
90 +| `Microsoft.VSTS.Common.ValueArea` | `Business` (field default) |
91 +| `Custom.HaveyouattachedEnglishandlocalizedscreenshots` | `No` (allowed: `Yes`/`No`; no default — must be set) |
92 +| `Custom.Language` | picklist — see mapping below (the WSL locale code is **not** accepted) |
93 +
94 +Other required custom flags (`Custom.BugBlocked`, `Custom.XLanguage`,
95 +`Custom.OverrideMaxStringsThreshold`, `Custom.SkipSourceAudioFilesValidation`,
96 +`Custom.DidCopilotrespondinthesamelanguage`, `Custom.Copilotattachallfiles`) default to `0` and
97 +auto-fill; leave them unset.
98 +
99 +### `Custom.Language` mapping (picklist values, not locale codes)
100 +
101 +`Custom.Language` is a 148-value picklist keyed by display name. Map the WSL `Resources.resw` locale
102 +folder to the picklist value:
103 +
104 +| WSL locale | `Custom.Language` value |
105 +|---|---|
106 +| `es-ES` | `Spanish (Spain, International Sort)` |
107 +| `zh-CN` | `Chinese (Simplified) - PRC` |
108 +| `el-GR` | `Greek (Greece)` |
109 +| `tr-TR` | `Turkish (Turkey)` |
110 +
111 +For other locales, list the allowed values and match by display name:
112 +
113 +```powershell
114 +$org="https://dev.azure.com/GlobalCollaborationService"
115 +$projEnc="Global%20Collaboration%20Service%20Project"
116 +$res="499b84ac-1321-427f-aa17-267ca6975798" # Azure DevOps AAD resource id
117 +az rest --method get --resource $res `
118 + --uri "$org/$projEnc/_apis/wit/workitemtypes/Bug/fields/Custom.Language?`$expand=all" `
119 + --headers "Accept=application/json;api-version=7.1-preview.3" |
120 + ConvertFrom-Json | Select-Object -ExpandProperty allowedValues
121 +```
122 +
123 +### Suggested description body
124 +
125 +```
126 +Community localization report on GitHub microsoft/WSL needs review by the loc pipeline.
127 +
128 +GitHub: https://github.com/microsoft/WSL/<issues or pull>/<number>
129 +Language: <locale>
130 +Type: <issue reporting a bad translation | PR editing the localized file>
131 +Scope: localized file (localization/strings/<locale>/Resources.resw or intune/<locale>/WSL.adml).
132 + For a PR: <edits to existing | brand-new>, +A/-B, 1 file. For an issue: the specific string(s) reported.
133 +Resource IDs to fix: <explicit list of <data name> keys - see the dedicated comment with the full
134 + list, or paste it here>. This is required; see "Deriving the resource-ID list" above.
135 +Action: validate placeholder/locked-token parity vs en-US (tools/devops/validate-localization.py),
136 +then incorporate or advise. New locales additionally need build registration, not just a file drop.
137 +Note: do not merge the GitHub PR - localized files are regenerated by GCS and edits here are overwritten.
138 +```
139 +
140 +The resource-ID list is the part GCS actually needs. If it is long, post it as a work-item **comment**
141 +(ADO comments accept HTML and can be edited in place via `PATCH .../comments/<id>`) and reference that
142 +comment from the description.
143 +
144 +### Creating via CLI (`az boards`)
145 +
146 +Requires the `azure-devops` az extension and an identity with **create/edit work item** permission in
147 +the project (see caveat below).
148 +
149 +```powershell
150 +$org="https://dev.azure.com/GlobalCollaborationService"
151 +$proj="Global Collaboration Service Project"
152 +$area="Global Collaboration Service Project\Global\Windows" # working area path, see Permissions gotcha
153 +$desc="Community es-ES localization PR. PR: https://github.com/microsoft/WSL/pull/14109 ..."
154 +az boards work-item create --org $org --project $proj --type "Bug" `
155 + --title "WSL: Review community es-ES localization PR (GitHub #14109)" `
156 + --area "$area" --description $desc `
157 + --fields "Microsoft.VSTS.TCM.ReproSteps=$desc" `
158 + "Custom.Language=Spanish (Spain, International Sort)" `
159 + "Custom.IssueType=Incorrect Translation" `
160 + "Custom.ProductArea=Software" `
161 + "Custom.How_Found=Community" `
162 + "Custom.Severity_Impact=S3 - Medium" `
163 + "Microsoft.VSTS.Common.ValueArea=Business" `
164 + "Custom.HaveyouattachedEnglishandlocalizedscreenshots=No"
165 +```
166 +
167 +### Gotchas
168 +
169 +- **`az.cmd` eats `&` in URLs.** When using `az rest`, never put `&` (multiple query params) in
170 + `--uri` on Windows — cmd truncates the URL there. Pass `api-version` via the **Accept header**
171 + instead: `--headers "Accept=application/json;api-version=7.1"`, keeping at most one `?param` in the URI.
172 +- **Permissions are area-path-scoped, and the path matters.** Creating work items requires explicit rights on
173 + the *specific area path*, not the project as a whole. The correct, durable path for WSL localization bugs is
174 + `Global Collaboration Service Project\Global\Windows` (confirmed against existing manually-filed WSL loc
175 + bugs). Always set `System.AreaPath` (or `--area`) to that path. A `@ntdev.microsoft.com` identity is denied
176 + at the project root and most other paths (`TF237111: ... does not have permissions to save work items under
177 + the specified area path`). Do NOT file under `...\C and AI\Unspecified`: an identity may briefly appear to
178 + have access there, but bugs filed there get cleaned up and the access is not stable. To probe a path without
179 + creating anything, POST with `?validateOnly=true`. If your identity lacks rights everywhere, create the bug
180 + through the web template link above.
181 +- **`System.Description` is required, separately from repro steps.** The Bug type rejects a create with
182 + `TF401320: Rule Error for field Description ... Required, InvalidEmpty` unless `System.Description` is set.
183 + Set both `System.Description` and `Microsoft.VSTS.TCM.ReproSteps` (the same body is fine). When POSTing a
184 + JSON-patch document via `az rest`, use newline-to-`<br>` so the HTML-rendered fields keep their line breaks.
185 +- **Picklist discovery.** Field allowed-values often don't expand through the plain field endpoint; use
186 + `?$expand=all` on `.../workitemtypes/Bug/fields/<ref>` as shown above.
187 +- **"Not found" can mean "no access," not "doesn't exist."** WIQL and `GET .../workitems/<id>` honor ACLs:
188 + a `CreatedBy=@me` query returning zero rows, or `TF401232: Work item <id> does not exist, or you do not
189 + have permissions to read it`, often just means your identity can't read that area path. Don't assume the
190 + bug was deleted. A gap in sequential work-item IDs is a tell that items were created but are now invisible
191 + to you (e.g. filed under a path whose access got revoked, then reaped) - which is exactly why you should
192 + stick to the durable `Global\Windows` path above.
193 +- **Read the bug back after creating it.** Immediately `GET` each new work item id (or open the URL) to
194 + confirm it's actually persisted and readable under your identity. A create that "succeeds" against a flaky
195 + area path can vanish later; a read-back catches that while you can still re-file.
196 +- **Non-ASCII gets mangled, and `az` errors poison `ConvertFrom-Json`.** Locale content (Turkish, Greek, CJK)
197 + comes back cp1252-garbled unless you set `[Console]::OutputEncoding=[System.Text.Encoding]::UTF8` first.
198 + Also, `az` prints errors to the stream, so piping straight into `ConvertFrom-Json` throws on any failure;
199 + capture with `Out-String` and parse, or check `$LASTEXITCODE`. Pass JSON-patch bodies via `--body "@file"`
200 + (content type `application/json-patch+json`) rather than inline to dodge quoting hell.
201 +- **Don't inline huge diffs into a comment.** For a PR that touches many strings, post the resource-ID
202 + list and link the PR "Files changed" page for the current→proposed values; a 100+ row HTML table
203 + makes the bug unreadable and can bump against comment-size limits. ADO comments can be edited in
204 + place (`PATCH .../comments/<id>?api-version=7.1-preview.4`), so trim/replace rather than re-posting.
205 +
206 +### After creating
207 +
208 +Keep the GitHub link in the ADO Bug's description/repro steps (it's already there from the body above) so the
209 +loc team can trace back to the report. Do **not** paste the internal ADO Bug ID into the public GitHub PR or
210 +issue. Then read each new bug back (see gotcha above) to confirm it persisted under `Global\Windows`.
211 +
212 +If GCS moves a bug to **More Info**, it almost always means the resource-ID detail is missing or too
213 +abstract: supply the explicit `<data name>` list (see "Deriving the resource-ID list"), then move the
214 +bug back to **Active** and reassign it to the requesting GCS reviewer.
215 +
216 +### What happens after you file (automated pipeline)
217 +
218 +Filing the Bug under `Global\Windows` is usually the end of the manual work. A **Fabric Self Service** bot
219 +(`FabricClient-Prod`) automatically picks the Bug up and creates a **DTT (Direct-To-Translator) job** for the
220 +locale, posting its progress as Bug discussion comments. So a stream of `FabricClient-Prod` comments is the
221 +**expected, healthy** path — not something a human has to act on:
222 +
223 +- `Bug picked up for processing by the Fabric Self Service process.`
224 +- `Successfully created 1 DTT Jobs: ... <locale> ...` → the report is now in the translation pipeline; **no
225 + further action**.
226 +
227 +Watch for these exceptions, which **do** need action:
228 +
229 +- **New locale not onboarded.** If the locale isn't onboarded to **TDBuild** for the WSL subtenant
230 + (`WindowsUndocking` / `LiftedWSL`), the DTT job can't be created and a human comments pointing you to file an
231 + onboarding request at the **Global Services Hub** (`https://globalservices.powerappsportals.com/global/`).
232 + This is GCS-side onboarding and is *separate from* (and in addition to) the WSL-repo build registration a new
233 + locale needs. Greek (`el-GR`, GitHub #40244) hit exactly this.
234 +- **Oversized report.** A non-retriable `Rqf query exceeded threshold of 1000 words` failure means the report's
235 + scope is too large; reduce the scope or set `Custom.OverrideMaxStringsThreshold` and re-file.
236 +
237 +If you only see `FabricClient-Prod` "Successfully created ... DTT Jobs" comments, you're done.
CONTRIBUTING.md
+6
@@ -21,6 +21,12 @@ You can file issues for WSL at the WSL repository, or linked repositories. Befor
21 2. If your issue is related to a Linux GUI app, please file it at [microsoft/wslg](https://github.com/microsoft/wslg/issues)
22 3. Otherwise, if you have a technical issue related to WSL in general, such as start up issues, etc., please file it at [microsoft/wsl](https://github.com/microsoft/WSL/issues)
23
24 +### Localization and translation issues
25 +
26 +If you spot a bad, missing, or incorrect translation in WSL's UI, **please file an issue** describing the problem (which language, what string, and what it should say) rather than opening a pull request.
27 +
28 +WSL's localized files are generated by a separate localization team and are regenerated downstream, so any translation edits made in a pull request here would be overwritten and cannot be merged. This covers the per-locale UI strings (`localization/strings/<locale>/Resources.resw`) and the per-locale Intune/group-policy templates (`intune/<locale>/WSL.adml`). Filing an issue lets us route the fix to the team that owns those translations. The `en-US` source files (`localization/strings/en-US/Resources.resw`, `intune/en-US/WSL.adml`, and the language-neutral `intune/WSL.admx`) are the exception, since those originate in this repository.
29 +
30 Please provide as much information as possible when reporting a bug or filing an issue on the Windows Subsystem for Linux, and be sure to include logs as necessary!
31
32 Please see the [notes for collecting WSL logs](#notes-for-collecting-wsl-logs) section below for more info on filing issues.