Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Translation] Make CrowdinProvider::write() add the locales missing from the project - #66028

Open
Amoifr wants to merge 1 commit into
symfony:8.2from
Amoifr:feat-64155-crowdin-write-missing-locales
Open

[Translation] Make CrowdinProvider::write() add the locales missing from the project#66028
Amoifr wants to merge 1 commit into
symfony:8.2from
Amoifr:feat-64155-crowdin-write-missing-locales

Conversation

@Amoifr

@Amoifr Amoifr commented Sep 12, 2026

Copy link
Copy Markdown
Contributor
Q A
Branch? 8.2
Bug fix? no
New feature? yes
Deprecations? no
Issues Part of #64155
License MIT

This covers the Crowdin write() item of the checklist in #64155. Loco, Lokalise and Phrase create the locales a project is missing, Crowdin logged Ignored "%s" locale because it is not configured or mapped in the project. and skipped them.

write() now adds the target languages the project does not have yet, in one PATCH on the project before the upload, keeping the languages already configured:

[{"op": "replace", "path": "/targetLanguageIds", "value": ["fr", "de"]}]

The locale is spelled as a Crowdin language ID, with a dash where Symfony uses an underscore, which is the reverse of what getLanguageIds() already does when reading them. A locale that the project maps under another name is left alone, since it is already known.

Editing a project requires a token with a read and write project.settings scope, which is the concern raised in the issue. A narrower token cannot add anything, so the failure is logged and write() then skips those locales exactly as it does today: no exception, and the locales that do exist are still pushed.

Tests cover both paths, the project being patched with the union of its languages, and the 403 case where the locales are skipped and the source file is still written. Checked with mocked responses only, not against a live Crowdin project.

@nicolas-grekas

Copy link
Copy Markdown
Member

Rebased on 8.2 and amended before merge.

The locale to language id conversion was str_replace("_", "-", $locale). Of the 314 languages Crowdin lists, only es-ES, la-LA and pt-PT use that shape, so fr_FR, de_DE, it_IT, ja_JP and most European locales produced an id that does not exist and the patch came back with Language fr-FR not found. write() now reads GET /languages once, only when a locale is missing, and maps a locale to an id through the locale field each language carries, preferring an exact id match for the three ambiguous cases.

Unsupported locales are now left out of the patch instead of travelling with the others, so a single unknown locale no longer stops every other one from being added; they keep falling back to the Ignored "%s" locale ... warning. A locale whose language the project already has, such as fr_FR against fr, no longer triggers a patch at all.

A 5xx on either request now throws like the rest of the bridge, while a 403 still only logs and skips. Added an UPGRADE-8.2.md note about the project.settings write scope, plus four tests.

I left out an RFC 6902 test op guarding the read-modify-write window: test on an array compares member by member including order, nothing promises Crowdin returns targetLanguageIds in a stable order, and a Crowdin that does not honour it would turn every push into a 400. The window is one round trip.

@Amoifr

Amoifr commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the rebase and the amend, and above all for catching the language id mapping. I had assumed str_replace('_', '-', $locale) was enough, and reading GET /languages once is clearly the right call when only 3 of the 314 languages use that shape. Leaving unsupported locales out of the patch instead of letting one of them sink the others is a real improvement too.

On the current red, Psalm fails on the new getSupportedLanguageIds():

CrowdinProvider.php:563 InvalidReturnType: declared 'array<string, string>', got 'array<array<array-key, string>|string, mixed>'

$language['data']['locale'] comes out of toArray() as mixed, so Psalm allows str_replace() to return an array and never narrows the key to string. Casting before use should settle it:

foreach ($response->toArray()['data'] as $language) {
    $id = (string) $language['data']['id'];
    $languageIds[str_replace('-', '_', (string) $language['data']['locale'])] ??= $id;
    $languageIds[str_replace('-', '_', $id)] = $id;
}

Want me to push that, or would you rather fold it into your amend?

The two unit test reds look unrelated: the same testSchemeRedirect and DoctrineOutboxIntegrationTest failures show on #65989, and both were fixed on 8.2 right after this run (6cc2cdda and b96ffaca). A rebase should clear them.

…rom the project

Loco, Lokalise and Phrase create the locales a project does not have yet, while
Crowdin logged them as ignored and skipped them. The project is now patched with
the languages it is missing before the upload.

Crowdin spells its language IDs its own way, so the list of the languages it
supports is fetched to turn a locale into an ID: "fr_FR" is the "fr" language and
"es_ES" is the "es-ES" one. A locale Crowdin does not know is left out of the
patch, so the other ones are still added, and it keeps falling back to the
warning write() already logged.

Editing a project needs a token with a read and write "project.settings" scope,
so a failure is logged and those locales are skipped as they were before, rather
than throwing. A server error still throws, like the rest of the bridge.
@Amoifr
Amoifr force-pushed the feat-64155-crowdin-write-missing-locales branch from 56cc42b to 1caeba4 Compare September 13, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants