Skip to content

Commit 5593848

Browse files
authored
[Blueprints] setSiteLanguage fetch translation package URL from WP.org (#81)
## Motivation for the change, related issues Before this PR the `setSiteLanguage` step automatically tried to resolve the WordPress core translation version, but some combinations of versions and languages don't have translation packages. This PR uses the WordPress.org translations API to get the correct translation URL for the given WordPress version and language. If a translation package doesn't exist, the step will still fail. ## Implementation details Instead of inferring the WordPress version, `setSiteLanguage` now uses the `api.wordpress.org/translations/core/1.0` endpoint to get the correct translation URL for the given WordPress version and language. This approach ensures we return an existing translation URL whenever possible and moves the WP version resolution to the WordPress.org translations API. ## Testing Instructions (or ideally a Blueprint) - CI
1 parent a88b704 commit 5593848

File tree

3 files changed

+50
-119
lines changed

3 files changed

+50
-119
lines changed
Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,53 @@
11
import { getWordPressTranslationUrl } from './set-site-language';
22

3-
describe('getTranslationUrl()', () => {
3+
describe('getWordPressTranslationUrl()', () => {
44
[
55
{
66
versionString: '6.2',
7-
latestBetaVersion: '6.6-RC',
8-
latestMinifiedVersion: '6.5.2',
9-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.2/en_US.zip`,
107
description:
11-
'should return a major.minor translation URL when the input version string is in a major.minor format',
8+
'should return a translation URL when the input version string is in a major.minor format',
129
},
1310
{
1411
versionString: '6.2.1',
15-
latestBetaVersion: '6.3.1-RC',
16-
latestMinifiedVersion: '6.4.2',
17-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.2.1/en_US.zip`,
1812
description:
19-
'should return a major.minor.patch translation URL when the input version string is in a major.minor.patch format',
13+
'should return a translation URL when the input version string is in a major.minor.patch format',
2014
},
2115
{
2216
versionString: '6.6-RC1',
23-
latestBetaVersion: '6.6-RC1',
24-
latestMinifiedVersion: '6.5.2',
25-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.6-RC/en_US.zip`,
26-
description:
27-
'should return the latest RC translation URL for a RC version',
17+
description: 'should return a translation URL for a RC version',
2818
},
2919
{
3020
versionString: '6.6-beta2',
31-
latestBetaVersion: '6.6-RC',
32-
latestMinifiedVersion: '6.5.2',
33-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.6-RC/en_US.zip`,
34-
description:
35-
'should return the latest RC translation URL for a beta version',
21+
description: 'should return a translation URL for a beta version',
3622
},
3723
{
3824
versionString: '6.6-nightly',
39-
latestBetaVersion: '6.6-RC',
40-
latestMinifiedVersion: '6.5.2',
41-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.6-RC/en_US.zip`,
4225
description:
43-
'should return the latest RC translation URL for a nightly version',
26+
'should return a translation URL for a nightly version',
4427
},
4528
{
4629
versionString: '6.8-alpha-59408',
47-
latestBetaVersion: '6.8-RC',
48-
latestMinifiedVersion: '6.7.2',
49-
expectedUrl: `https://downloads.wordpress.org/translation/core/6.8-RC/en_US.zip`,
50-
description:
51-
'should return the latest RC translation URL for an alpha version',
30+
description: 'should return a translation URL for an alpha version',
5231
},
53-
].forEach(
54-
({
55-
versionString,
56-
latestBetaVersion,
57-
latestMinifiedVersion,
58-
expectedUrl,
59-
description,
60-
}) => {
61-
it(description, () => {
62-
expect(
63-
getWordPressTranslationUrl(
64-
versionString,
65-
'en_US',
66-
latestBetaVersion,
67-
latestMinifiedVersion
68-
)
69-
).resolves.toBe(expectedUrl);
70-
});
71-
}
72-
);
32+
].forEach(({ versionString, description }) => {
33+
it(description, async () => {
34+
const url = await getWordPressTranslationUrl(
35+
versionString,
36+
'es_PE'
37+
);
38+
expect(url).toMatch(
39+
/^https:\/\/downloads\.wordpress\.org\/translation\/core\/[\d.]+\/es_PE\.zip$/
40+
);
41+
});
42+
});
43+
44+
it('should throw an error if the translation package is not found', async () => {
45+
/**
46+
* en_US is the default language, so there are no translations available
47+
* for it.
48+
*/
49+
await expect(
50+
getWordPressTranslationUrl('6.6-RC', 'en_US')
51+
).rejects.toThrow();
52+
});
7353
});

packages/playground/blueprints/src/lib/steps/set-site-language.ts

Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { StepHandler } from '.';
22
import { unzipFile } from '@wp-playground/common';
33
import { logger } from '@php-wasm/logger';
4-
import { resolveWordPressRelease } from '@wp-playground/wordpress';
54
import { Semaphore } from '@php-wasm/util';
6-
75
/**
86
* @inheritDoc setSiteLanguage
97
* @hasRunnableExample
@@ -23,79 +21,32 @@ export interface SetSiteLanguageStep {
2321
}
2422

2523
/**
26-
* Infers the translation package URL for a given WordPress version.
24+
* Get the translation package URL for a given WordPress version and language.
25+
* The translation package URL is fetched from the WordPress.org API based on
26+
* the provided WordPress version.
2727
*
28-
* If it cannot be inferred, the latest translation package will be used instead.
28+
* If the translation package is not found, an error is thrown.
2929
*/
3030
export const getWordPressTranslationUrl = async (
3131
wpVersion: string,
32-
language: string,
33-
latestBetaWordPressVersion?: string,
34-
latestStableWordPressVersion?: string
35-
) => {
36-
/**
37-
* Infer a WordPress version we can feed into the translations API based
38-
* on the requested fully-qualified WordPress version.
39-
*
40-
* The translation API provides translations for:
41-
*
42-
* - all major.minor WordPress releases
43-
* - all major.minor.patch WordPress releases
44-
* - Latest beta/RC version – under a label like "6.6-RC". It's always "-RC".
45-
* There's no "-BETA1", "-RC1", "-RC2", etc.
46-
*
47-
* The API does not provide translations for "nightly", "latest", or
48-
* old beta/RC versions.
49-
*
50-
* For example translations for WordPress 6.6-BETA1 or 6.6-RC1 are found under
51-
* https://downloads.wordpress.org/translation/core/6.6-RC/en_GB.zip
52-
*/
53-
let resolvedVersion = null;
54-
if (wpVersion.match(/^(\d+\.\d+)(?:\.\d+)?$/)) {
55-
// Use the version directly if it's a major.minor or major.minor.patch.
56-
resolvedVersion = wpVersion;
57-
} else if (wpVersion.match(/^(\d.\d(.\d)?)-(beta|rc|alpha|nightly).*$/i)) {
58-
// Translate "6.4-alpha", "6.5-beta", "6.6-nightly", "6.6-RC" etc.
59-
// to "6.6-RC"
60-
if (latestBetaWordPressVersion) {
61-
resolvedVersion = latestBetaWordPressVersion;
62-
} else {
63-
let resolved = await resolveWordPressRelease('beta');
64-
// Beta versions are only available during the beta period –
65-
// let's use the latest stable release as a fallback.
66-
if (resolved.source !== 'api') {
67-
resolved = await resolveWordPressRelease('latest');
68-
}
69-
resolvedVersion = resolved!.version;
70-
}
71-
resolvedVersion = resolvedVersion
72-
// Remove the patch version, e.g. 6.6.1-RC1 -> 6.6-RC1
73-
.replace(/^(\d.\d)(.\d+)/i, '$1')
74-
// Replace "rc" and "beta" with "RC", e.g. 6.6-nightly -> 6.6-RC
75-
.replace(/(rc|beta).*$/i, 'RC');
76-
} else {
77-
/**
78-
* Use the latest stable version otherwise.
79-
*
80-
* The requested version is neither stable, nor beta/RC, nor alpha/nightly.
81-
* It must be a custom version string. We could actually fail at this point,
82-
* but it seems more useful to* download translations from the last official
83-
* WordPress version. If that assumption is wrong, let's reconsider this whenever
84-
* someone reports a related issue.
85-
*/
86-
if (latestStableWordPressVersion) {
87-
resolvedVersion = latestStableWordPressVersion;
88-
} else {
89-
const resolved = await resolveWordPressRelease('latest');
90-
resolvedVersion = resolved!.version;
91-
}
92-
}
93-
if (!resolvedVersion) {
32+
language: string
33+
): Promise<string> => {
34+
const languageTranslations = await fetch(
35+
`https://api.wordpress.org/translations/core/1.0/?version=${wpVersion}`
36+
);
37+
const languageTranslationsJson = await languageTranslations.json();
38+
const languageTranslation = languageTranslationsJson.translations.find(
39+
(translation: any) =>
40+
translation.language.toLowerCase() === language.toLowerCase()
41+
);
42+
43+
if (!languageTranslation) {
9444
throw new Error(
95-
`WordPress version ${wpVersion} is not supported by the setSiteLanguage step`
45+
`Failed to get ${language} translation package for WordPress ${wpVersion}.`
9646
);
9747
}
98-
return `https://downloads.wordpress.org/translation/core/${resolvedVersion}/${language}.zip`;
48+
49+
return languageTranslation.package;
9950
};
10051

10152
/**

packages/playground/website/playwright/e2e/blueprints.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -480,15 +480,15 @@ test('should correctly redirect to a multisite wp-admin url', async ({
480480
await expect(wordpress.locator('body')).toContainText('General Settings');
481481
});
482482

483-
['latest', 'nightly', 'beta'].forEach((version) => {
484-
test(`should translate WP-admin to Spanish for the ${version} WordPress build`, async ({
483+
['latest', 'nightly', 'beta'].forEach((wpVersion) => {
484+
test(`should translate WP-admin to Spanish for the ${wpVersion} WordPress build`, async ({
485485
website,
486486
wordpress,
487487
}) => {
488488
const blueprint: Blueprint = {
489489
landingPage: '/wp-admin/',
490490
preferredVersions: {
491-
wp: 'nightly',
491+
wp: wpVersion,
492492
},
493493
steps: [{ step: 'setSiteLanguage', language: 'es_ES' }],
494494
};

0 commit comments

Comments
 (0)