master
md 237 lines 14.6 KB
Rendered Raw
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.