Skip to content

Studio: Translate "About Studio" modal - #228

Merged
katinthehatsite merged 6 commits into
trunkfrom
add/translate-about-window
Jun 11, 2024
Merged

Studio: Translate "About Studio" modal#228
katinthehatsite merged 6 commits into
trunkfrom
add/translate-about-window

Conversation

@katinthehatsite

@katinthehatsite katinthehatsite commented Jun 10, 2024

Copy link
Copy Markdown
Contributor

Closes https://github.com/Automattic/dotcom-forge/issues/7522

Proposed Changes

This PR makes the strings in the "About Studio" window translatable.

Testing Instructions

  • Pull the changes from this branch locally
  • Before starting the app, modify the language in locale.ts in getPreferredSystemLanguages to return French:
export function getPreferredSystemLanguages() {
	if ( process.platform === 'linux' && process.env.NODE_ENV !== 'test' ) {
			.getPreferredSystemLanguages()
			.filter( ( lang ) => supportedLocales.includes( lang ) );
	}
	return [ 'fr' ];
}
  • Navigate to src/translations/studio-fr.jed.json
  • Add the following strings and translations to the file:
            "Demo sites powered by": [
                "Sites de démonstration propulsés par"
            ],
            "Local sites powered by": [
                "Sites locaux propulsés par"
            ],
            "Share Feedback": [
                "Partager des commentaires"
            ],
            "Studio by WordPress.com": [
                "Studio par WordPress.com"
            ],
            "Version": [
                "Version"
            ]
  • Start the app with nvm use && npm install && npm start
  • Open the About Studio window
  • Confirm that the strings are translated to French:
Capture d’écran, le 2024-06-10 à 13 46 09

Notes:

  • I explored refactoring the HTML file into a React component and then rendering it into this custom window but the solution required modifying the app setup in multiple places. I opted for a more straightforward approach considering that that window is fairly static

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@katinthehatsite
katinthehatsite requested a review from a team June 10, 2024 11:53
<p class="studio-name" id="studio-by-wpcom">Studio by WordPress.com</p>
<p class="version"><span id="version-text">Version</span> <span id="version"></span></p>
<p class="links">
<a href="https://github.com/Automattic/studio" target="_blank">GitHub</a>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not add translation for GitHub since it is a proper name. Happy to add it if it is considered necessary.

@sejas sejas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've followed the testing instructions and it works as expected.

Screenshot 2024-06-10 at 23 09 48

I wanted to recommend using esc_attr__ , but it's not part of @wordpress/i18n.
I suggest adding Sentry or sanitizing the strings with a custom function. My concern is that some translation would include a single quote: ' , which would break the string replacements.

Comment thread src/about-menu/open-about-menu.ts Outdated
document.getElementById('local-sites').innerText = '${ __( 'Local sites powered by' ) }';`
)
.catch( ( err ) => {
console.error( 'Error executing JavaScript:', err );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we capture the error in sentry?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 343ac4d

Comment thread src/about-menu/open-about-menu.ts Outdated
Comment on lines +44 to +46
`document.getElementById('version').innerText = '${ packageJson }';
document.getElementById('studio-by-wpcom').innerText = '${ __( 'Studio by WordPress.com' ) }';
document.getElementById('version-text').innerText = '${ __( 'Version' ) }';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's unify the word Version and the actual number. Something like sprintf( __( 'Version %s' ), packageJson )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in af02dbb

Comment thread src/about-menu/about-menu.html Outdated
<p class="studio-name">Studio by WordPress.com</p>
<p class="version">Version <span id="version"></span></p>
<p class="studio-name" id="studio-by-wpcom">Studio by WordPress.com</p>
<p class="version"><span id="version-text">Version</span> <span id="version"></span></p>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use only one string here, so RTL languages will work better.

Suggested change
<p class="version"><span id="version-text">Version</span> <span id="version"></span></p>
<p class="version"><span id="version-text">Version x.y.z</span></p>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in af02dbb

@katinthehatsite

Copy link
Copy Markdown
Contributor Author

I suggest adding Sentry or sanitizing the strings with a custom function. My concern is that some translation would include a single quote: ' , which would break the string replacements.

Yeah I think you are right, I tried to break it and it did break. Let me conjure something for this. I will re-request review once I am ready

@katinthehatsite

katinthehatsite commented Jun 11, 2024

Copy link
Copy Markdown
Contributor Author

@sejas I added something simple in 6c1f64f to handle the ' to ensure that it is rendered.

I also found that we have something like https://www.npmjs.com/package/@wordpress/escape-html that we already have available and installed but a single quote breaks when I try to use escapeAtrribute or escapeHMTL. Do you think the fix about would be sufficient for this case in combination with Sentry capture?

Comment on lines +40 to +42
function escapeSingleQuotes( str: string ) {
return str.replace( /'/g, "\\'" );
}

@sejas sejas Jun 11, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes!

Nice! I tested it and it renders the single quote and it doesn't break the About page.
I tried replacing to &quot;, but it didn't render the correct entity.

Screenshot 2024-06-11 at 13 48 14

I've also seen that @wordpress/escape-html escapeAttribute Only escapes, the >, " and & symbols.

@katinthehatsite
katinthehatsite merged commit 216154a into trunk Jun 11, 2024
@katinthehatsite
katinthehatsite deleted the add/translate-about-window branch June 11, 2024 13:52

@wojtekn wojtekn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explored refactoring the HTML file into a React component and then rendering it into this custom window but the solution required modifying the app setup in multiple places. I opted for a more straightforward approach considering that that window is fairly static

Thanks for sharing the details explaining the approach you took. It seems reasonable to do so.

</div>
<div class="info">
<p>Local sites powered by<br><a href="https://wordpress.org/playground/" target="_blank">WordPress Playground ↗</a></p>
<p><span id="local-sites">Local sites powered by</span><br><a href="https://wordpress.org/playground/" target="_blank">WordPress Playground ↗</a></p>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need those phrases in two places: here in the template and in the file that replaces those with translations?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean why the translation is divided in two "divs"?
<p><span id="local-sites">Local sites powered by</span><br><a href="https://wordpress.org/playground/" target="_blank">WordPress Playground ↗</a></p> instead of <p id="local-sites">Local sites powered by<br><a href="https://wordpress.org/playground/" target="_blank">WordPress Playground ↗</a></p>

When reviewing I thought it could produce errors in RTL languages but the fact that we display one on top of the other solves any issue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I meant that tests, e.g., Local sites powered by, are now placed in two places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing that your question is if it could be removed from https://github.com/Automattic/studio/blob/c2c9c20219ea884493bd3ba8ffbc64f2bc5cc1ce/src/about-menu/about-menu.html#L90 since we are inserting it anyway so that it would be something like:

 <p><span id="demo-sites"></span><br><a href="https://wordpress.com/hosting/?utm_source=studio&utm_medium=referral&utm_campaign=about_screen" target="_blank">WordPress.com hosting ↗</a></p>

(so just the id and no text)

Is that right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is correct. I was wondering if, with the current implementation, there is a reason to keep it in the file template, too. Does it work as a fallback, so if the translation couldn't be loaded, we would display the English version included in the template? Or could such a situation not occur?

My point is that keeping the source text in two places comes with a risk that someone updates it in one place, skipping the latter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. The strings in the HTML file serve as fallback content. This ensures that if the JavaScript fails to inject the translations or if there is any issue with the translation strings, the user will still see the default text that is added in the HTML file.

However, you bring up an important point - it might not be obvious that the code needs to be handled in two places. I will create a PR to add clarifying comments to this code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a PR here to add some clarifying comments:

#290

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for following up on this and adding those comments. It will be helpful for future us!

wojtekn added a commit that referenced this pull request Jun 3, 2026
## Related issues

Closes ~35 open Dependabot security alerts on the repository.

## How AI was used in this PR

Claude identified which Dependabot alerts were fixable via `npm update`
(stale lockfile entries within existing semver ranges) vs. those
requiring direct dep bumps or overrides, then applied all the easy wins
in one shot.

## Proposed Changes

Updates transitive and direct dependencies to their patched versions to
close the majority of open Dependabot security alerts. All updates are
within the existing declared semver ranges of their parent packages — no
breaking changes expected.

| Package | From | To | Alerts closed |
|---------|------|----|---------------|
| `fast-uri` | 3.1.0 | 3.1.2 |
[#210](https://github.com/Automattic/studio/security/dependabot/210),
[#207](https://github.com/Automattic/studio/security/dependabot/207) |
| `@xmldom/xmldom` | 0.8.11 | 0.8.13 |
[#192](https://github.com/Automattic/studio/security/dependabot/192),
[#191](https://github.com/Automattic/studio/security/dependabot/191),
[#190](https://github.com/Automattic/studio/security/dependabot/190),
[#170](https://github.com/Automattic/studio/security/dependabot/170) |
| `brace-expansion` | 1.1.12 | 1.1.15 |
[#168](https://github.com/Automattic/studio/security/dependabot/168) |
| `qs` | 6.14.0 | 6.15.2 |
[#228](https://github.com/Automattic/studio/security/dependabot/228),
[#89](https://github.com/Automattic/studio/security/dependabot/89),
[#110](https://github.com/Automattic/studio/security/dependabot/110) |
| `ip-address` | 10.1.0 | 10.1.1 |
[#199](https://github.com/Automattic/studio/security/dependabot/199) |
| `basic-ftp` | 5.3.0 | 5.3.1 |
[#202](https://github.com/Automattic/studio/security/dependabot/202) |
| `ws` | 8.18.0 | 8.21.0 |
[#224](https://github.com/Automattic/studio/security/dependabot/224) |
| `protobufjs` | 7.5.5 | 7.6.2 |
[#221](https://github.com/Automattic/studio/security/dependabot/221),
[#220](https://github.com/Automattic/studio/security/dependabot/220),
[#217](https://github.com/Automattic/studio/security/dependabot/217),
[#216](https://github.com/Automattic/studio/security/dependabot/216),
[#219](https://github.com/Automattic/studio/security/dependabot/219),
[#218](https://github.com/Automattic/studio/security/dependabot/218),
[#215](https://github.com/Automattic/studio/security/dependabot/215),
[#225](https://github.com/Automattic/studio/security/dependabot/225) |
| `hono` | 4.12.7 | 4.12.23 |
[#211](https://github.com/Automattic/studio/security/dependabot/211),
[#208](https://github.com/Automattic/studio/security/dependabot/208),
[#204](https://github.com/Automattic/studio/security/dependabot/204),
[#203](https://github.com/Automattic/studio/security/dependabot/203),
[#188](https://github.com/Automattic/studio/security/dependabot/188),
[#185](https://github.com/Automattic/studio/security/dependabot/185),
[#184](https://github.com/Automattic/studio/security/dependabot/184),
[#183](https://github.com/Automattic/studio/security/dependabot/183),
[#182](https://github.com/Automattic/studio/security/dependabot/182),
[#181](https://github.com/Automattic/studio/security/dependabot/181),
[#209](https://github.com/Automattic/studio/security/dependabot/209) |
| `@hono/node-server` | 1.19.11 | 1.19.14 |
[#180](https://github.com/Automattic/studio/security/dependabot/180) |
| `lodash` | 4.17.23 | 4.18.1 |
[#172](https://github.com/Automattic/studio/security/dependabot/172),
[#171](https://github.com/Automattic/studio/security/dependabot/171) |
| `@babel/plugin-transform-modules-systemjs` | 7.27.1 | 7.29.7 |
[#212](https://github.com/Automattic/studio/security/dependabot/212) |
| `@anthropic-ai/sdk` | ^0.90.0 | ^0.91.1 |
[#197](https://github.com/Automattic/studio/security/dependabot/197),
[#196](https://github.com/Automattic/studio/security/dependabot/196) |

**Remaining open alerts** are blocked by exact-pinned third-party
packages (`@php-wasm/*`, `@mariozechner/pi-*`, `electron2appx`) or have
no upstream fix (`showdown`). Those require upstream releases to
resolve.

## Testing Instructions

- [ ] `npm run typecheck` passes
- [ ] `npm test` passes
- [ ] App starts and sites load normally

## Pre-merge Checklist

- [x] No direct dependency version constraints changed (all updates are
lockfile-only except `@anthropic-ai/sdk` range bump)
- [x] `@wp-playground/*` and `@php-wasm/*` pins untouched
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants