Skip to content

Commit

Permalink
SX Design: merge translations when FBT instance already exists
Browse files Browse the repository at this point in the history
This change fixes a support for applications that are already using FBT. The problem was that FBT uses a singleton pattern and doesn't work well when you are calling FBT `init` (or `registerTranslations`) inside another `init` because it gets overwritten. Applications not using FBT work without any issues.

PR facebook/fbt@d05d44f adds necessary changes to FBT and this change implements them in SX Design.

Closes: #2205
  • Loading branch information
mrtnzlml authored and kodiakhq[bot] committed May 4, 2021
1 parent a2e0b4f commit 4001a56
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 77 deletions.
7 changes: 2 additions & 5 deletions src/abacus-backoffice/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import initTranslations from '../translations/init';
export default function MyApp({ Component, pageProps }: $FlowFixMe): React.Node {
const router = useRouter();
// $FlowIssue[prop-missing] prop missing in flow-typed types
initTranslations(router.locale);
const locale = initTranslations(router.locale);

const [hasMounted, setHasMounted] = useState(false);
const { sessionToken } = useSessionTokenAPI();
Expand Down Expand Up @@ -51,10 +51,7 @@ export default function MyApp({ Component, pageProps }: $FlowFixMe): React.Node
});

return (
<SxDesignProvider
locale="en-US" // TODO
theme="system"
>
<SxDesignProvider locale={locale} theme="system">
<ErrorBoundary>
<RelayEnvironmentProvider environment={relayEnvironment}>
<RecoilRoot>{children}</RecoilRoot>
Expand Down
2 changes: 1 addition & 1 deletion src/abacus-backoffice/translations/in/es-MX.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"fb-locale": "es-mx",
"fb-locale": "es-MX",
"translations": {
"XKQt/WzTgsZemYhMzTVAuw==": {
"tokens": [],
Expand Down
11 changes: 8 additions & 3 deletions src/abacus-backoffice/translations/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import { IntlVariations, init as fbtInit } from 'fbt';

export default function initTranslations(locale: 'en-us' | 'es-mx'): void {
export default function initTranslations(nextjsLocale: 'en-us' | 'es-mx'): 'en-US' | 'es-MX' {
// TODO: do this Next.js -> FBT conversion better once needed
const locale = nextjsLocale === 'es-mx' ? 'es-MX' : 'en-US';

const supportedLocales = {
'en-us': require('./out/en-US.json'),
'es-mx': require('./out/es-MX.json'),
'en-US': require('./out/en-US.json'),
'es-MX': require('./out/es-MX.json'),
};

fbtInit({
Expand All @@ -17,4 +20,6 @@ export default function initTranslations(locale: 'en-us' | 'es-mx'): void {
}),
},
});

return locale;
}
2 changes: 1 addition & 1 deletion src/abacus-backoffice/translations/out/en-US.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"en-us": {}
"en-US": {}
}
2 changes: 1 addition & 1 deletion src/abacus-backoffice/translations/out/es-MX.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"es-mx": {
"es-MX": {
"3JkX8r": "Ha ocurrido un error desconocido",
"eiaXU": "Cerrar sesión",
"2eLf8v": "List of employees",
Expand Down
19 changes: 2 additions & 17 deletions src/kochka.com.mx/pages/_app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @flow

import * as React from 'react';
import App from 'next/app';
import fbt from 'fbt';
import Head from 'next/head';
import ReactDOM from 'react-dom';
Expand Down Expand Up @@ -30,10 +29,9 @@ type Props = {
+pageProps: any,
};

function MyApp({ Component, pageProps }: Props): React.Node {
export default function MyApp({ Component, pageProps }: Props): React.Node {
const router = useRouter();
/* $FlowFixMe[prop-missing] This comment suppresses an error when migrating
* to adeira/universe. To see the error delete this comment and run Flow. */
// $FlowFixMe[prop-missing]: `locale` is missing in `flow-typed` definitions
const languageTag = initFbtTranslations(router.locale);

const isProduction = __DEV__ === false;
Expand Down Expand Up @@ -117,16 +115,3 @@ const styles = sx.create({
fontStyle: 'italic',
},
});

// TODO: remove:
//
// This disables the ability to perform automatic static optimization, causing every page in
// the app to be server-side rendered (needed for the translations to be properly loaded).
//
MyApp.getInitialProps = async (appContext: $FlowFixMe): $FlowFixMe => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps };
};

export default MyApp;
2 changes: 1 addition & 1 deletion src/kochka.com.mx/translations/initFbtTranslations.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export default function initFbtTranslations(lang: ?string): LanguageTagType {
const languageTag = LanguageTag.detectLanguageTag(lang);
const locale = languageTag.bcp47;

// TODO: support translations lazy loading
const supportedLocales = {
// TODO: support translations lazy loading
'en-US': require('./out/en-US.json'), // empty stub
'es-MX': require('./out/es-MX.json'),
};
Expand Down
8 changes: 4 additions & 4 deletions src/kochka.com.mx/translations/source_strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,9 @@
"+9If2Wz1/auQYgoiMqI3JQ==": "coming soon"
},
"filepath": "pages/_app.js",
"line_beg": 48,
"line_beg": 46,
"col_beg": 14,
"line_end": 48,
"line_end": 46,
"col_end": 55,
"desc": "coming soon",
"project": "",
Expand All @@ -635,9 +635,9 @@
"9NSktf8Wasdbln+avfGiWQ==": "Skip to content"
},
"filepath": "pages/_app.js",
"line_beg": 74,
"line_beg": 72,
"col_beg": 20,
"line_end": 76,
"line_end": 74,
"col_end": 26,
"desc": "hidden 'skip link' title which helps blind people to skip directly the main section and avoid the repetitive menu altogether",
"project": "",
Expand Down
36 changes: 19 additions & 17 deletions src/sx-design/src/Money/__tests__/Money.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,27 @@ import SxDesignProvider from '../../SxDesignProvider';
// When adding new currencies, always add one line for `en-US` and one line for the new locale.
// This way we can test that it works well for both natives and foreigners.
test.each`
locale | amount | currency | expectedReact | expectedFn
${'en-US'} | ${10} | ${'AED'} | ${'AED 10.00'} | ${'AED 10.00'}
${'ar-AR'} | ${10} | ${'AED'} | ${'د.إ.‏ 10.00'} | ${'د.إ.‏ 10.00'}
${'ar-AR-u-nu-arab'} | ${10} | ${'AED'} | ${'١٠٫٠٠ د.إ.‏'} | ${'١٠٫٠٠ د.إ.‏'}
${'en-US'} | ${10} | ${'CZK'} | ${'CZK 10.00'} | ${'CZK 10.00'}
${'cs-CZ'} | ${10} | ${'CZK'} | ${'10,00 Kč'} | ${'10,00 Kč'}
${'en-US'} | ${20} | ${'USD'} | ${'$20.00'} | ${'$20.00'}
${'es-MX'} | ${20} | ${'USD'} | ${'USD 20.00'} | ${'USD 20.00'}
${'en-US'} | ${10} | ${'MXN'} | ${'MX$10.00'} | ${'MX$10.00'}
${'es-MX'} | ${10} | ${'MXN'} | ${'$10.00'} | ${'$10.00'}
${'en-US'} | ${10} | ${'NOK'} | ${'NOK 10.00'} | ${'NOK 10.00'}
${'no-NO'} | ${10} | ${'NOK'} | ${'kr 10,00'} | ${'kr 10,00'}
${'en-US'} | ${10} | ${'RUB'} | ${'RUB 10.00'} | ${'RUB 10.00'}
${'ru-RU'} | ${10} | ${'RUB'} | ${'10,00 ₽'} | ${'10,00 ₽'}
${'en-US'} | ${10} | ${'UAH'} | ${'UAH 10.00'} | ${'UAH 10.00'}
${'uk-UA'} | ${10} | ${'UAH'} | ${'10,00 ₴'} | ${'10,00 ₴'}
locale | currency | expectedReact | expectedFn
${'en-US'} | ${'AED'} | ${'AED 10.00'} | ${'AED 10.00'}
${'ar-AR'} | ${'AED'} | ${'د.إ.‏ 10.00'} | ${'د.إ.‏ 10.00'}
${'ar-AR-u-nu-arab'} | ${'AED'} | ${'١٠٫٠٠ د.إ.‏'} | ${'١٠٫٠٠ د.إ.‏'}
${'en-US'} | ${'CZK'} | ${'CZK 10.00'} | ${'CZK 10.00'}
${'cs-CZ'} | ${'CZK'} | ${'10,00 Kč'} | ${'10,00 Kč'}
${'en-US'} | ${'USD'} | ${'$10.00'} | ${'$10.00'}
${'es-MX'} | ${'USD'} | ${'USD 10.00'} | ${'USD 10.00'}
${'en-US'} | ${'MXN'} | ${'MX$10.00'} | ${'MX$10.00'}
${'es-MX'} | ${'MXN'} | ${'$10.00'} | ${'$10.00'}
${'en-US'} | ${'NOK'} | ${'NOK 10.00'} | ${'NOK 10.00'}
${'no-NO'} | ${'NOK'} | ${'kr 10,00'} | ${'kr 10,00'}
${'en-US'} | ${'RUB'} | ${'RUB 10.00'} | ${'RUB 10.00'}
${'ru-RU'} | ${'RUB'} | ${'10,00 ₽'} | ${'10,00 ₽'}
${'en-US'} | ${'UAH'} | ${'UAH 10.00'} | ${'UAH 10.00'}
${'uk-UA'} | ${'UAH'} | ${'10,00 ₴'} | ${'10,00 ₴'}
`(
'renders amount "$amount" with locale "$locale" and currency "$currency" correctly ("$expectedFn")',
({ locale, amount, currency, expectedReact, expectedFn }) => {
({ locale, currency, expectedReact, expectedFn }) => {
const amount = 10;

const { getByText } = render(
<SxDesignProvider locale={locale}>
<Money priceUnitAmount={amount} priceUnitAmountCurrency={currency} />
Expand Down
55 changes: 28 additions & 27 deletions src/sx-design/src/SxDesignProvider.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// @flow

import React, { useMemo, type Node, useEffect, useState } from 'react';
import { init as fbtInit, IntlVariations } from 'fbt';
import React, { useMemo, useEffect, useState, type Node } from 'react';
import { init as FbtInit, IntlVariations as FbtIntlVariations, FbtTranslations } from 'fbt';
import sx from '@adeira/sx';

import getFbtTranslationsForLocale from './getFbtTranslationsForLocale';
import SxDesignContext from './SxDesignContext';
import SxDesignProviderCSSVariables from './SxDesignProviderCSSVariables';
import type { SupportedLocales } from './constants';
Expand All @@ -23,7 +24,7 @@ export default function SxDesignProvider(props: Props): Node {
: 'light';
});

const locale = props.locale ?? 'en-US';
const sxLocale = props.locale ?? 'en-US';
const theme = props.theme ?? 'light';

useEffect(() => {
Expand All @@ -36,42 +37,42 @@ export default function SxDesignProvider(props: Props): Node {
return () => {};
}, []);

// TODO: support translations lazy loading
const supportedLocales = {
'ar-AR': require('../translations/out/ar-AR.json'),
'ar-AR-u-nu-arab': require('../translations/out/ar-AR.json'),
'cs-CZ': require('../translations/out/cs-CZ.json'),
'en-US': require('../translations/out/en-US.json'),
'es-MX': require('../translations/out/es-MX.json'),
'no-NO': require('../translations/out/no-NO.json'),
'ru-RU': require('../translations/out/ru-RU.json'),
'uk-UA': require('../translations/out/uk-UA.json'),
};

let direction = 'ltr';
if (locale.startsWith('ar-AR')) {
if (sxLocale.startsWith('ar-AR')) {
direction = 'rtl';
}

fbtInit({
translations: supportedLocales[locale],
hooks: {
getViewerContext: () => ({
GENDER: IntlVariations.GENDER_UNKNOWN,
locale,
}),
},
});
const allFbtTranslations = FbtTranslations.getRegisteredTranslations();
if (allFbtTranslations[sxLocale] != null) {
// There is already a translation dictionary registered for this locale (probably from the
// parent application) so we simply extend this disctionary with SX Design specific strings.
FbtTranslations.mergeTranslations(
getFbtTranslationsForLocale(
((sxLocale: any): SupportedLocales), // see the `invariant` above
),
);
} else {
// There is no dictionary for this locale yet so we initialize it with SX Design strings.
FbtInit({
translations: getFbtTranslationsForLocale(sxLocale),
hooks: {
getViewerContext: () => ({
GENDER: FbtIntlVariations.GENDER_UNKNOWN,
locale: sxLocale,
}),
},
});
}

const contextValue = useMemo(
() => ({
locale,
locale: sxLocale,
direction,
// Here we exchange "system" value for the actual theme because underlying components need to
// know only whether we are rendering "light" or "dark".
theme: theme === 'system' ? actualSystemColor : theme,
}),
[locale, direction, theme, actualSystemColor],
[actualSystemColor, direction, sxLocale, theme],
);

return (
Expand Down
34 changes: 34 additions & 0 deletions src/sx-design/src/getFbtTranslationsForLocale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @flow

import { invariant } from '@adeira/js';

import type { SupportedLocales } from './constants';

type TranslationStr = string;

// {locale: {hash: translation}}
type TranslationDict = {
[locale: string]: { [hashKey: string]: TranslationStr },
};

export default function getFbtTranslationsForLocale(locale: SupportedLocales): TranslationDict {
// TODO: support translations lazy loading
const supportedLocales = {
'ar-AR': require('../translations/out/ar-AR.json'),
'ar-AR-u-nu-arab': require('../translations/out/ar-AR.json'),
'cs-CZ': require('../translations/out/cs-CZ.json'),
'en-US': require('../translations/out/en-US.json'),
'es-MX': require('../translations/out/es-MX.json'),
'no-NO': require('../translations/out/no-NO.json'),
'ru-RU': require('../translations/out/ru-RU.json'),
'uk-UA': require('../translations/out/uk-UA.json'),
};

invariant(
supportedLocales[locale] != null,
'Cannot get FBT translation for locale "%s" because it doesn\'t exist.',
locale,
);

return supportedLocales[locale];
}

0 comments on commit 4001a56

Please sign in to comment.