Creating Localization Tracking Bugs (GCS Azure DevOps)
Community members report localization problems in two ways: by filing a GitHub issue about a bad
translation, or by opening a pull request that edits a localized file (the per-locale UI strings in
localization/strings/<locale>/Resources.resw, or the per-locale Intune/group-policy templates in
intune/<locale>/WSL.adml), or that adds a brand-new locale file. Both are valid signals and you should
look for both when triaging.
Either way, the fix is handled by the Microsoft Global Collaboration Service (GCS) localization pipeline,
not merged directly from GitHub: every non-en-US localized file is generated by the GCS team and
regenerated downstream, so any translation edit merged here would be overwritten. (PRs that touch
localized files should be declined with a pointer to CONTRIBUTING.md; only the en-US sources and the
language-neutral intune/WSL.admx live in this repo.) To route a report for review, file a tracking
Bug in the GCS Azure DevOps project for each issue or PR.
GCS requirement — be specific: list the exact resource IDs to fix. The loc team will not triage abstract reports. A bug that only names the file, language, and a few quoted strings gets rejected (moved to the More Info state) because the team does not have capacity to parse a vague report into the concrete strings that need work — that translation has to be done up front by the filer. Every bug must therefore carry an explicit list of the resource IDs (the
nameattribute of the<data name="...">elements inlocalization/strings/<locale>/Resources.resw) that need fixing. The gold-standard level of detail is Bug 1223866 (.../GlobalCollaborationService/Global%20Collaboration%20Service%20Project/_workitems/edit/1223866): for each resource ID it gives the current (bad) value and the proposed value, e.g.MessageDistroAlreadySet current: Der Verteilungsname wurde bereits festgelegt. proposed: Der Distributionsname wurde bereits festgelegt.
Deriving the resource-ID list
How you obtain the IDs depends on the report type:
- PR editing an existing locale file — the changed resource IDs are exactly the
<data name>blocks whose<value>differs between the repo's current file and the PR head. Load both and diff by name. The PR "Files changed" view already shows current→proposed for each, so post the compact ID list plus a link to the PR diff rather than re-inlining every value (a 160-row inline table is unwieldy; the PR diff is the source of truth for the old/new text).
# changed resource IDs between two Resources.resw files (master vs PR head)
function Get-ChangedIds($baseResw, $headResw) {
function Load($p) { $x = [xml](Get-Content $p -Raw); $h = @{}; foreach ($d in $x.root.data) { $h[$d.name] = $d.value }; $h }
$b = Load $baseResw; $h = Load $headResw
$h.Keys | Where-Object { $b[$_] -ne $h[$_] }
}
Issue citing line numbers (e.g.
Resources.resw#L185) — fetch the file at the exact commit the issue links to, then walk upward from each cited line to the nearestdata name="..."to get the enclosing resource ID. Line numbers drift between commits, so always pin to the linked SHA.Issue quoting bad strings — grep the locale
Resources.reswfor each quoted value to find the<data name>that contains it. One displayed term often maps to several IDs (.Header,.Description,...TextBox.AutomationProperties.Name/.HelpText, shell-nav.Content), so list them all.PR adding a brand-new locale — this is a net-new whole-file contribution, not a correction list, so there is nothing pre-existing to "fix". Give the team the actionable file-level items instead: (1) hard parity failures vs
en-USthat blocktools/devops/validate-localization.py— placeholder/insert-count mismatches and missing{Locked="..."}tokens, listed by resource ID; (2) the coverage gap (whichen-USIDs are missing, and how many of N total); (3) any strings present in the file but absent fromen-US(obsolete — drop them); and (4) that a new locale needs build registration, not just a file drop.
Coordinates
| Org | https://dev.azure.com/GlobalCollaborationService |
| Project | Global Collaboration Service Project |
| Process | GCS_Agile |
| Work item type | Bug |
| Web "create from template" link | https://dev.azure.com/GlobalCollaborationService/Global%20Collaboration%20Service%20Project/_workitems/create/Bug?templateId=b97df4d3-4106-4099-a3a9-1782b5891bec |
Required fields (and known-good values for a community loc PR)
The Bug type has several required custom picklist fields. Values that work for "community translation PR needs review":
| Field (reference name) | Value |
|---|---|
System.Title |
WSL: Review community <locale> localization report (GitHub #<issue or PR>) |
System.Description / Microsoft.VSTS.TCM.ReproSteps |
GitHub link + scope + action (see below) |
Custom.IssueType |
Incorrect Translation |
Custom.ProductArea |
Software |
Custom.How_Found |
Community |
Custom.Severity_Impact |
S3 - Medium (field default) |
Microsoft.VSTS.Common.ValueArea |
Business (field default) |
Custom.HaveyouattachedEnglishandlocalizedscreenshots |
No (allowed: Yes/No; no default — must be set) |
Custom.Language |
picklist — see mapping below (the WSL locale code is not accepted) |
Other required custom flags (Custom.BugBlocked, Custom.XLanguage,
Custom.OverrideMaxStringsThreshold, Custom.SkipSourceAudioFilesValidation,
Custom.DidCopilotrespondinthesamelanguage, Custom.Copilotattachallfiles) default to 0 and
auto-fill; leave them unset.
Custom.Language mapping (picklist values, not locale codes)
Custom.Language is a 148-value picklist keyed by display name. Map the WSL Resources.resw locale
folder to the picklist value:
| WSL locale | Custom.Language value |
|---|---|
es-ES |
Spanish (Spain, International Sort) |
zh-CN |
Chinese (Simplified) - PRC |
el-GR |
Greek (Greece) |
tr-TR |
Turkish (Turkey) |
For other locales, list the allowed values and match by display name:
$org="https://dev.azure.com/GlobalCollaborationService"
$projEnc="Global%20Collaboration%20Service%20Project"
$res="499b84ac-1321-427f-aa17-267ca6975798" # Azure DevOps AAD resource id
az rest --method get --resource $res `
--uri "$org/$projEnc/_apis/wit/workitemtypes/Bug/fields/Custom.Language?`$expand=all" `
--headers "Accept=application/json;api-version=7.1-preview.3" |
ConvertFrom-Json | Select-Object -ExpandProperty allowedValues
Suggested description body
Community localization report on GitHub microsoft/WSL needs review by the loc pipeline.
GitHub: https://github.com/microsoft/WSL/<issues or pull>/<number>
Language: <locale>
Type: <issue reporting a bad translation | PR editing the localized file>
Scope: localized file (localization/strings/<locale>/Resources.resw or intune/<locale>/WSL.adml).
For a PR: <edits to existing | brand-new>, +A/-B, 1 file. For an issue: the specific string(s) reported.
Resource IDs to fix: <explicit list of <data name> keys - see the dedicated comment with the full
list, or paste it here>. This is required; see "Deriving the resource-ID list" above.
Action: validate placeholder/locked-token parity vs en-US (tools/devops/validate-localization.py),
then incorporate or advise. New locales additionally need build registration, not just a file drop.
Note: do not merge the GitHub PR - localized files are regenerated by GCS and edits here are overwritten.
The resource-ID list is the part GCS actually needs. If it is long, post it as a work-item comment
(ADO comments accept HTML and can be edited in place via PATCH .../comments/<id>) and reference that
comment from the description.
Creating via CLI (az boards)
Requires the azure-devops az extension and an identity with create/edit work item permission in
the project (see caveat below).
$org="https://dev.azure.com/GlobalCollaborationService"
$proj="Global Collaboration Service Project"
$area="Global Collaboration Service Project\Global\Windows" # working area path, see Permissions gotcha
$desc="Community es-ES localization PR. PR: https://github.com/microsoft/WSL/pull/14109 ..."
az boards work-item create --org $org --project $proj --type "Bug" `
--title "WSL: Review community es-ES localization PR (GitHub #14109)" `
--area "$area" --description $desc `
--fields "Microsoft.VSTS.TCM.ReproSteps=$desc" `
"Custom.Language=Spanish (Spain, International Sort)" `
"Custom.IssueType=Incorrect Translation" `
"Custom.ProductArea=Software" `
"Custom.How_Found=Community" `
"Custom.Severity_Impact=S3 - Medium" `
"Microsoft.VSTS.Common.ValueArea=Business" `
"Custom.HaveyouattachedEnglishandlocalizedscreenshots=No"
Gotchas
az.cmdeats&in URLs. When usingaz rest, never put&(multiple query params) in--urion Windows — cmd truncates the URL there. Passapi-versionvia the Accept header instead:--headers "Accept=application/json;api-version=7.1", keeping at most one?paramin the URI.- Permissions are area-path-scoped, and the path matters. Creating work items requires explicit rights on
the specific area path, not the project as a whole. The correct, durable path for WSL localization bugs is
Global Collaboration Service Project\Global\Windows(confirmed against existing manually-filed WSL loc bugs). Always setSystem.AreaPath(or--area) to that path. A@ntdev.microsoft.comidentity is denied at the project root and most other paths (TF237111: ... does not have permissions to save work items under the specified area path). Do NOT file under...\C and AI\Unspecified: an identity may briefly appear to have access there, but bugs filed there get cleaned up and the access is not stable. To probe a path without creating anything, POST with?validateOnly=true. If your identity lacks rights everywhere, create the bug through the web template link above. System.Descriptionis required, separately from repro steps. The Bug type rejects a create withTF401320: Rule Error for field Description ... Required, InvalidEmptyunlessSystem.Descriptionis set. Set bothSystem.DescriptionandMicrosoft.VSTS.TCM.ReproSteps(the same body is fine). When POSTing a JSON-patch document viaaz rest, use newline-to-<br>so the HTML-rendered fields keep their line breaks.- Picklist discovery. Field allowed-values often don't expand through the plain field endpoint; use
?$expand=allon.../workitemtypes/Bug/fields/<ref>as shown above. - "Not found" can mean "no access," not "doesn't exist." WIQL and
GET .../workitems/<id>honor ACLs: aCreatedBy=@mequery returning zero rows, orTF401232: Work item <id> does not exist, or you do not have permissions to read it, often just means your identity can't read that area path. Don't assume the bug was deleted. A gap in sequential work-item IDs is a tell that items were created but are now invisible to you (e.g. filed under a path whose access got revoked, then reaped) - which is exactly why you should stick to the durableGlobal\Windowspath above. - Read the bug back after creating it. Immediately
GETeach new work item id (or open the URL) to confirm it's actually persisted and readable under your identity. A create that "succeeds" against a flaky area path can vanish later; a read-back catches that while you can still re-file. - Non-ASCII gets mangled, and
azerrors poisonConvertFrom-Json. Locale content (Turkish, Greek, CJK) comes back cp1252-garbled unless you set[Console]::OutputEncoding=[System.Text.Encoding]::UTF8first. Also,azprints errors to the stream, so piping straight intoConvertFrom-Jsonthrows on any failure; capture withOut-Stringand parse, or check$LASTEXITCODE. Pass JSON-patch bodies via--body "@file"(content typeapplication/json-patch+json) rather than inline to dodge quoting hell. - Don't inline huge diffs into a comment. For a PR that touches many strings, post the resource-ID
list and link the PR "Files changed" page for the current→proposed values; a 100+ row HTML table
makes the bug unreadable and can bump against comment-size limits. ADO comments can be edited in
place (
PATCH .../comments/<id>?api-version=7.1-preview.4), so trim/replace rather than re-posting.
After creating
Keep the GitHub link in the ADO Bug's description/repro steps (it's already there from the body above) so the
loc team can trace back to the report. Do not paste the internal ADO Bug ID into the public GitHub PR or
issue. Then read each new bug back (see gotcha above) to confirm it persisted under Global\Windows.
If GCS moves a bug to More Info, it almost always means the resource-ID detail is missing or too
abstract: supply the explicit <data name> list (see "Deriving the resource-ID list"), then move the
bug back to Active and reassign it to the requesting GCS reviewer.
What happens after you file (automated pipeline)
Filing the Bug under Global\Windows is usually the end of the manual work. A Fabric Self Service bot
(FabricClient-Prod) automatically picks the Bug up and creates a DTT (Direct-To-Translator) job for the
locale, posting its progress as Bug discussion comments. So a stream of FabricClient-Prod comments is the
expected, healthy path — not something a human has to act on:
Bug picked up for processing by the Fabric Self Service process.Successfully created 1 DTT Jobs: ... <locale> ...→ the report is now in the translation pipeline; no further action.
Watch for these exceptions, which do need action:
- New locale not onboarded. If the locale isn't onboarded to TDBuild for the WSL subtenant
(
WindowsUndocking/LiftedWSL), the DTT job can't be created and a human comments pointing you to file an onboarding request at the Global Services Hub (https://globalservices.powerappsportals.com/global/). This is GCS-side onboarding and is separate from (and in addition to) the WSL-repo build registration a new locale needs. Greek (el-GR, GitHub #40244) hit exactly this. - Oversized report. A non-retriable
Rqf query exceeded threshold of 1000 wordsfailure means the report's scope is too large; reduce the scope or setCustom.OverrideMaxStringsThresholdand re-file.
If you only see FabricClient-Prod "Successfully created ... DTT Jobs" comments, you're done.