Skip to content

Commit

Permalink
Add getFragment method to @fluent/react
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinnl committed Apr 3, 2023
1 parent 387c630 commit 450256c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 13 deletions.
15 changes: 6 additions & 9 deletions frontend/src/components/dashboard/ProfileBanners.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,16 @@ const BounceBanner = (props: BounceBannerProps) => {

return (
<Banner type="warning" title={l10n.getString("banner-bounced-headline")}>
<Localized
id="banner-bounced-copy"
vars={{
{l10n.getFragment("banner-bounced-copy", {
vars: {
username: props.email,
bounce_type: props.profile.bounce_status[1],
date: renderDate(props.profile.next_email_try, l10n),
}}
elems={{
},
elems: {
em: <em />,
}}
>
<p />
</Localized>
},
})}
</Banner>
);
};
Expand Down
36 changes: 32 additions & 4 deletions frontend/src/hooks/l10n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,27 @@
// should be used instead, but of course this hook can use it just fine:
// eslint-disable-next-line no-restricted-imports
import { ReactLocalization, useLocalization } from "@fluent/react";
import { useEffect, useState } from "react";
import { createElement, Fragment, useEffect, useState } from "react";

/**
* Equivalent to ReactLocalization.getString, but returns a React Fragment.
*
* This is useful because it allows you to replace tags in localised strings
* with HTML elements, without needing to reach out to <Localized>.
*
* (This method got booted out of @fluent/react proper because it's so simple,
* but it's pretty useful:
* https://github.com/projectfluent/fluent.js/pull/595#discussion_r967011632)
*/
type GetFragment = (
id: Parameters<ReactLocalization["getString"]>[0],
args?: Parameters<ReactLocalization["getElement"]>[2],
fallback?: Parameters<ReactLocalization["getString"]>[2]
) => ReturnType<ReactLocalization["getElement"]>;

type ExtendedReactLocalization = ReactLocalization & {
getFragment: GetFragment;
};

/**
* Wraps @fluent/react's useLocalization to be consistent between prerender and first render
Expand All @@ -17,16 +37,19 @@ import { useEffect, useState } from "react";
* rendered when both prerendering and doing the first client-side render, and
* only after that use the language that aligns with the user's preferences.
*/
export const useL10n = (): ReactLocalization => {
export const useL10n = (): ExtendedReactLocalization => {
const { l10n } = useLocalization();
const [isPrerendering, setIsPrerendering] = useState(true);

const getFragment: GetFragment = (id, args, fallback) =>
l10n.getElement(createElement(Fragment, null, fallback ?? id), id, args);

useEffect(() => {
setIsPrerendering(false);
}, []);

if (isPrerendering) {
const prerenderingL10n: ReactLocalization = {
const prerenderingL10n: ExtendedReactLocalization = {
getBundle: l10n.getBundle,
bundles: l10n.bundles,
parseMarkup: l10n.parseMarkup,
Expand All @@ -44,10 +67,15 @@ export const useL10n = (): ReactLocalization => {
return l10n.getString(id, vars, fallback);
},
getElement: l10n.getElement,
getFragment: getFragment,
};

return prerenderingL10n;
}

return l10n;
const extendedL10n: ExtendedReactLocalization =
l10n as ExtendedReactLocalization;
extendedL10n.getFragment = getFragment;

return extendedL10n;
};

0 comments on commit 450256c

Please sign in to comment.