Central hub for the localization of the Power BI custom visuals owned by Microsoft.
English strings are collected from the visual repositories into this repo, sent for translation, and the finished translations are pushed back to every visual repository. The whole cycle is automated; this repo holds the master copy of the translated resources plus the automation that moves them around.
| Path | Purpose |
|---|---|
localizations/<visual>/stringResources/<locale>/resources.resjson |
Master copy of the strings for every visual and locale. Source of truth. |
visuals/<visual> |
Git submodules pointing at the shipping visual repositories. Used only as transport (read en-US from them, write translations back). |
descriptions/<visual>/en-US.resjson |
English marketplace descriptions. |
deprecatedLocalizations/ |
Strings of visuals that are no longer shipped. |
src/loc/<locale>/<visual>/en-US/resources.resjson.lcl |
LocStudio output produced by OneLocBuild (English source paired with its translation). |
LocProject.json |
OneLocBuild configuration: maps each localizations/.../en-US/resources.resjson to its .lcl file. |
.github/workflows/ |
Harvest and distribute workflows (GitHub Actions). |
.pipelines/ |
Azure DevOps pipeline that runs OneLocBuild. |
src/commit-and-open-pr.js |
Creates verified commits through the GitHub GraphQL API and makes sure a pull request is open for them. |
src/parseNewLocalizations.js |
Copies the OneLocBuild output back into localizations/ (see npm scripts). |
flowchart TD
A[Visual repos: en-US strings change] --> B[harvest-en-strings.yml<br/>daily 07:00 UTC]
B --> C[PR 'new_translations' into main<br/>updates localizations/**/en-US]
C --> D[merge]
D --> E[Azure DevOps: pipeline.localization.utils.yml<br/>OneLocBuild]
E --> F[PR 'parsed_localizations'<br/>updated translations for all locales]
F --> G[merge]
G --> H[distribute-translations.yml<br/>push to main with translated locale changes]
H --> I[PR 'new_translations' in each visual repo]
Harvest — .github/workflows/harvest-en-strings.yml
runs on a daily schedule (visual repos cannot notify this repo directly) or on demand.
It updates the submodules, copies each visuals/<visual>/stringResources/en-US/resources.resjson
into localizations/, and opens a PR on the fixed branch new_translations. The branch
name is fixed on purpose: the existing PR is reused, so review comments and CLA status survive.
Translate — the Azure DevOps pipeline
.pipelines/pipeline.localization.utils.yml
triggers on main, runs OneLocBuild@2 against LocProject.json, then
npm run parseNewLocalizations copies the translated files from loc/<locale>/localizations/...
back into localizations/<visual>/stringResources/<locale>/. It opens a PR on the fixed
branch parsed_localizations, reusing the existing one when it is still open.
Distribute — .github/workflows/distribute-translations.yml
triggers on a push to main touching non-en-US files under localizations/** (i.e. when a
translation PR merges), or on demand. It copies every translated locale from
localizations/<visual>/stringResources into the matching submodule
and opens a new_translations PR in every visual repository. en-US is not distributed: it
is owned by the visual repository, so copying it back could revert English strings added
there since the last harvest. Files whose only difference is
the line ending are reverted before committing to avoid CRLF-only noise.
There is no separate schedule for distribution — the push event is the signal, and
workflow_dispatch covers retries after a partial failure.
Both automations act as a dedicated GitHub App rather than a user personal access token. The App is installed on this repository and on every visual repository, with Contents: write and Pull requests: write permissions.
- GitHub Actions mint an installation token per job with
actions/create-github-app-token@v2.repositoriesis intentionally omitted so the token covers every repo in the organization where the App is installed. - The Azure DevOps pipeline mints the same kind of token with .pipelines/get-github-app-token.ps1.
App credentials are stored as secrets in the GitHub environment / pipeline variable group and are never committed to this repository.
The microsoft organization requires signed commits. A GitHub App has no GPG key, so
git commit -S is not an option; src/commit-and-open-pr.js
creates the commit through the GraphQL createCommitOnBranch mutation, which GitHub signs
with its own key. Before committing it re-points the bot branch at the current base commit,
so the branch is always exactly one commit ahead of the default branch instead of
accumulating history against an ever older base. Content is read from the git index rather
than from disk, so .gitattributes filters apply to what is committed.
That re-point is a force move, so anything pushed onto a bot branch by hand is discarded on the next run.
Opening the pull request is part of the same script on purpose. Committing and opening a pull request are two separate API calls, so a run that fails between them would leave the content on a branch nobody looks at. The script therefore also opens a pull request when it had nothing new to commit but the bot branch is still ahead of the base, which repairs the previous run. Callers only have to check whether it succeeded.
- Add the visual repository as a submodule:
cd visuals git submodule add https://github.com/microsoft/<repo-name>
- Add a
LocItementry for the visual to LocProject.json. - Ask a maintainer to install the localization GitHub App on the new repository with Contents: write and Pull requests: write permissions.
- In the new repository: Settings > General > Pull Requests — enable Automatically delete head branches.
- Open a pull request with the changes above against
main.
Automatically delete head branches should also remain enabled in this repository. The automation reuses a fixed branch name and rebuilds it from the default branch on every run, so a branch left behind by a squash merge resolves itself, but deleting merged branches keeps the repository tidy.
| Script | Description |
|---|---|
npm run parseNewLocalizations |
Copies OneLocBuild output from loc/ into localizations/. Used by the ADO pipeline. |
Both workflows expose workflow_dispatch, so they can be started from the
Actions tab. Use this after a partial failure — the fixed new_translations
branch means a rerun updates the existing PR instead of creating a new one.
Distribute translations to visuals also takes an only input: a folder name under
visuals/, for example powerbi-visuals-asterplot, to retry a single repository instead
of all of them. Leave it empty to update every visual. A name that does not exist fails the
run rather than quietly updating nothing.