diff --git a/src/app/downloads/nightlies.tsx b/src/app/downloads/nightlies.tsx index 77d1069..a07231c 100644 --- a/src/app/downloads/nightlies.tsx +++ b/src/app/downloads/nightlies.tsx @@ -24,7 +24,7 @@ import { githubReleasesUrl, webLinks, } from "@/app/downloads/config"; -import { useTranslation } from "@/app/translate"; +import { useTranslation, Trans } from "@/app/translate"; function DownloadLink({ link, @@ -130,15 +130,19 @@ export function NightlyList({ {t("downloads.nightly-releases")} - {t("downloads.nightly-releases-description")}{" "} - - {t("footer.github")} - - {t("common.line-ender")} + + {t("footer.github")} + , + ]} + /> '} - {t("downloads.self-host-description-start")}{" "} - - {t("downloads.self-host-description-link")} - {" "} - {t("downloads.self-host-description-end")} + + {t("downloads.self-host-description-link")} + , + ]} + /> {''} - {t("downloads.advanced-usage-description-start")}{" "} - - {t("downloads.advanced-usage-description-link")} - {" "} - {t("downloads.advanced-usage-description-end")} + + {t("downloads.advanced-usage-description-link")} + , + ]} + /> ); diff --git a/src/app/translate.tsx b/src/app/translate.tsx index aef2812..5a113ef 100644 --- a/src/app/translate.tsx +++ b/src/app/translate.tsx @@ -111,6 +111,35 @@ export function useTranslation() { return { t }; } +interface TransProps { + i18nKey: string; // Translation key + components?: React.ReactNode[]; // Components to inject into placeholders +} + +export const Trans: React.FC = ({ i18nKey, components = [] }) => { + const { t } = useTranslation(); + const translation = t(i18nKey); + + const renderWithPlaceholders = (template: string) => { + const parts = template.split(/({{.*?}})/g); // Split on placeholders like {{key}} + return parts.map((part) => { + const match = part.match(/{{(.*?)}}/); // Match placeholders + if (match) { + const placeholderKey = match[1]; + const component = components.find( + (comp) => React.isValidElement(comp) && comp.key === placeholderKey, + ); + if (component) { + return component; + } + } + return part; // Return plain text if no placeholder + }); + }; + + return <>{renderWithPlaceholders(translation)}; +}; + export const LanguageSelector: React.FC = ({ className, }) => { diff --git a/src/i18n/translations.en.json b/src/i18n/translations.en.json index 6a31d0c..769eb52 100644 --- a/src/i18n/translations.en.json +++ b/src/i18n/translations.en.json @@ -64,20 +64,15 @@ "browser-extension": "Browser Extension", "browser-extension-description": "If you visit websites that have Flash content but aren't using Ruffle, or you want to ensure you're using the latest and greatest version of Ruffle on every website, then our browser extension is the perfect thing for you!", "nightly-releases": "Nightly Releases", - "nightly-releases-description": "If none of the above are suitable for you, you can manually download the latest Nightly release. These are automatically built every day (approximately midnight UTC), unless there are no changes on that day. Older nightly releases are available on", + "nightly-releases-description": "If none of the above are suitable for you, you can manually download the latest Nightly release. These are automatically built every day (approximately midnight UTC), unless there are no changes on that day. Older nightly releases are available on {{link}}.", "web-package": "Web Package", "web-package-description": "You can install Ruffle onto a website using one single line of code by using a CDN, no extra work required! It'll always stay up to date with the latest available version of Ruffle.", - "self-host-description-start": "If you'd like to host it yourself, you can grab", + "self-host-description": "If you'd like to host it yourself, you can grab {{link}} and upload it to your server. Then, include it on your page like so:", "self-host-description-link": "the latest self-hosted package", - "self-host-description-end": "and upload it to your server. Then, include it on your page like so:", - "advanced-usage-description-start": "For advanced usage, consult", + "advanced-usage-description": "For advanced usage, consult {{link}} for our JavaScript API and installation options.", "advanced-usage-description-link": "our documentation", - "advanced-usage-description-end": "for our JavaScript API and installation options.", "chrome-extension-alt": "Available in the Chrome Web Store", "firefox-extension-alt": "Get the Add-On for Firefox", "edge-extension-alt": "Get it from Microsoft for Edge" - }, - "common": { - "line-ender": "." } }