-
Notifications
You must be signed in to change notification settings - Fork 116
Add Google Translate integration with language switcher #1909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
oceans404
wants to merge
9
commits into
main
Choose a base branch
from
feat/google-translate-integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
e0fd2c0
Add Google Translate integration with language switcher
oceans404 7319484
fix language switcher cookie bug and aria roles
oceans404 2ceabc4
update limits
oceans404 4825019
add slight background highlighting current language
oceans404 0495fe0
translate the word language but not each language
oceans404 f7abcec
remove text search in test
oceans404 181a3c1
make sure EURC and USDC are not translated
oceans404 7ff0b6b
block translate requests on playright
oceans404 766c419
make sure funded asset names aren't translated
oceans404 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import React, { Suspense } from "react"; | ||
| import type { Metadata } from "next"; | ||
| import { headers } from "next/headers"; | ||
|
|
||
| import { LayoutMain } from "@/components/layout/LayoutMain"; | ||
| import { LayoutContextProvider } from "@/components/layout/LayoutContextProvider"; | ||
|
|
@@ -9,6 +10,7 @@ import { CustomAiButton } from "@/components/CustomAiButton"; | |
| import { QueryProvider } from "@/query/QueryProvider"; | ||
| import { StoreProvider } from "@/store/StoreProvider"; | ||
| import { GoogleAnalytics } from "@/metrics/GoogleAnalytics"; | ||
| import { GoogleTranslateMountPoint } from "@/components/GoogleTranslateMountPoint"; | ||
|
|
||
| import "@stellar/design-system/build/styles.min.css"; | ||
| import "@/styles/globals.scss"; | ||
|
|
@@ -22,14 +24,49 @@ export const metadata: Metadata = { | |
| // Automatically generates nonce for script and style tags | ||
| export const dynamic = "force-dynamic"; | ||
|
|
||
| export default function RootLayout({ | ||
| export default async function RootLayout({ | ||
| children, | ||
| }: { | ||
| children: React.ReactNode; | ||
| }) { | ||
| const nonce = (await headers()).get("x-nonce") ?? ""; | ||
|
|
||
| return ( | ||
| <html lang="en"> | ||
| <body> | ||
| <html lang="en" suppressHydrationWarning> | ||
| <head> | ||
| {/* Google Translate init — must carry nonce to pass the app's CSP. | ||
| suppressHydrationWarning is required because browsers strip nonce | ||
| attributes from the DOM after load (to prevent CSS exfiltration), | ||
| so the client-side React tree sees nonce="" while the server-rendered | ||
| HTML has the real value. The nonce is still present in the raw HTML | ||
| that the browser parses for CSP enforcement before React runs. */} | ||
| <script | ||
| nonce={nonce} | ||
| suppressHydrationWarning | ||
| dangerouslySetInnerHTML={{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we really shouldn't use |
||
| __html: ` | ||
| function googleTranslateElementInit() { | ||
| var mount = document.getElementById('google_translate_element'); | ||
| if (!mount) { | ||
| requestAnimationFrame(googleTranslateElementInit); | ||
| return; | ||
| } | ||
| new google.translate.TranslateElement({ | ||
| pageLanguage: 'en', | ||
| autoDisplay: false, | ||
| }, 'google_translate_element'); | ||
| } | ||
| `, | ||
| }} | ||
| /> | ||
| <script | ||
| src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" | ||
| nonce={nonce} | ||
| suppressHydrationWarning | ||
| async | ||
| /> | ||
| </head> | ||
| <body suppressHydrationWarning> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. body doesn't need |
||
| <Suspense> | ||
| <div id="root"> | ||
| <StoreProvider> | ||
|
|
@@ -44,6 +81,7 @@ export default function RootLayout({ | |
| </StoreProvider> | ||
| </div> | ||
| <GoogleAnalytics /> | ||
| <GoogleTranslateMountPoint /> | ||
| </Suspense> | ||
| </body> | ||
| </html> | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /** | ||
| * Renders a Stellar asset code (USDC, EURC, etc.) as a non-translatable span. | ||
| * Asset codes are technical identifiers that should never be translated by | ||
| * browser translation engines like Google Translate. | ||
| */ | ||
| export const AssetCode = ({ children }: { children: React.ReactNode }) => ( | ||
| <span className="notranslate">{children}</span> | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect } from "react"; | ||
|
|
||
| /** | ||
| * Appends the Google Translate engine mount point to the document body after | ||
| * mount, keeping it entirely outside React's reconciliation tree. | ||
| * | ||
| * Rendering this div in JSX would SSR it as `<div hidden>`, but the Translate | ||
| * engine modifies it on the client (removes `hidden`, adds `class="skiptranslate"`, | ||
| * injects an `<iframe>`). React's reconciler then sees a mismatch and throws a | ||
| * recoverable hydration error. Creating the element imperatively in `useEffect` | ||
| * means it is never in the server-rendered HTML, so React never tries to reconcile it. | ||
| */ | ||
| export const GoogleTranslateMountPoint = () => { | ||
| useEffect(() => { | ||
| const el = document.createElement("div"); | ||
| el.id = "google_translate_element"; | ||
| document.body.appendChild(el); | ||
|
|
||
| return () => { | ||
| el.remove(); | ||
| }; | ||
| }, []); | ||
|
|
||
| return null; | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we really shouldn't enable
suppressHydrationWarning.