tachyon-intl
v32.0.0
Published
Wrapper around twitch-intl that consumes tachyon-intl-server data
Downloads
4
Readme
Tachyon-Intl
Internationalization helpers using twitch-intl that works well with tachyon-intl-server.
Setup
Add twitch-intl-cli To Your Project
If you have not yet had your Smartling project onboarded to Loom, reach out on Slack in the #loom channel.
In the package's
package.json
file add the necessary intl commands and dependencies, replacing$LOOM_PROJECT
with your Loom project id:{ "files": ["messages/"], "scripts": { "intl:download": "twitch-intl download --midway -S \"!(node_modules)/**/!(*.test).ts?(x)\" --project-id $LOOM_PROJECT --outdir ./messages", "intl:submit": "twitch-intl upload --midway -S \"!(node_modules)/**/!(*.test).ts?(x)\" --project-id $LOOM_PROJECT --prompt --exclude-uploaded --submit --jira-ticket", "intl:test": "yarn intl:test:ci --midway", "intl:test:ci": "twitch-intl submission-check -S \"!(node_modules)/**/!(*.test).ts?(x)\" --project-id $LOOM_PROJECT" }, "dependencies": { "tachyon-intl": "^17.0.0" }, "devDependencies": { "twitch-intl-cli": "^4.4.0" } }
Create a
messages
directory in the root of the project.Submit strings following the submission process.
Run
yarn intl:download
to populate messages for all locales.
Install The Root Context
Your app can either load intl data on its own and pass them into
TachyonIntlRoot
(this is what Tachyon apps do):
import type { FC } from 'react';
import { IntlData, TachyonIntlRoot } from 'tachyon-intl';
export const renderApp: FC = () => {
// Implement getIntlData reading it in the way that makes the most sense
// for your application
const intlData: IntlData = getIntlData();
return <TachyonIntlRoot data={intlData}>{/* App JSX */}</TachyonIntlRoot>;
};
If your app already uses TwitchIntl and needs to inter-op with tachyon-intl
,
you can pass in the intl instance (this is what Twilight does):
import type { FC } from 'react';
import { TachyonIntlRoot } from 'tachyon-intl';
export const renderApp: FC = () => {
// Wherever your intl comes from
const intl = createTwitchIntl();
return <TachyonIntlRoot intl={intl}>{/* App JSX */}</TachyonIntlRoot>;
};
Submitting Strings
Tachyon-Intl uses twitch-intl-cli to manage string submission. Submit strings for the "tachyon" (if developing in Tachyon) Loom project (corresponding to the "Mobile Web" Smartling project) following the string submission process.
Downloading Strings
yarn intl:download
Formatting App Strings For Internationalization
Reference this internationalization guide for an exhaustive list of localization use cases such as dealing with numbers, dates, times, durations, and link rendering in sentences.
Hook Use
- Call
useIntl()
in your functional component. - Destructure the necessary format helper(s) to a local variable named
formatMessage
(or one of the other names) off of the return ofuseIntl()
. - Read the documentation for using these helpers.
IMPORTANT: Do not skip step #2. If you attempt to consume formatMessage
directly off of intl
, twitch-intl-cli
will not function correctly. The
internationalization relies on the names matching exactly and being their own
function names.
import type { FC } from 'react';
import { useIntl } from 'tachyon-intl';
interface WelcomeBannerProps {}
export const WelcomeBanner: FC<WelcomeBannerProps> = (props) => {
// Always assign to a local variable before consuming
const { formatMessage, formatNumber, formatNumberShort } = useIntl();
return (
<div>
{formatMessage('Welcome to your home screen!', 'WelcomeBanner')}
{/* ... */}
</div>
);
};
HOC Use
- Wrap a component with the
withIntl
HOC and extendIntlProps
in the component's prop interface. - Assign the necessary format helper(s) to a local variable named
formatMessage
(or one of the other names) off ofprops.intl
. - Read the documentation for using these helpers.
IMPORTANT: Do not skip step #2. If you attempt to consume formatMessage
directly off of props.intl
, twitch-intl-cli
will not function correctly. The
internationalization relies on the names matching exactly and being their own
function names.
import { Component } from 'react';
import { IntlProps, withIntl } from 'tachyon-intl';
interface WelcomeBannerProps extends IntlProps {}
class WelcomeBannerBase extends Component<WelcomeBannerProps> {
public override render(): JSX.Element {
// Always assign to a local variable before consuming
const { formatMessage, formatNumber, formatNumberShort } = this.props.intl;
return (
<div>
{formatMessage('Welcome to your home screen!', 'WelcomeBanner')}
{/* ... */}
</div>
);
}
}
export const WelcomeBanner = withIntl(WelcomeBannerBase);
Getting The User's Selected Locale
You can also get the user's preferred, among those we support, locale and language:
import type { FC } from 'react';
import { useIntl } from 'tachyon-intl';
const LocaleDependentComponent: FC = () => {
const { getActiveLocale, getLanguageCode } = useIntl();
// ...
};
Gating Features Based On User Locale
The <LocaleGate>
allows gating features behind a list of country codes. By
default it operates in allowlist mode but it can also operate as a blocklist.
Operating as an Allowlist
import type { FC } from 'react';
import { LocaleGate, CountryCode } from 'tachyon-intl';
const ComponentThatShouldOnlyRenderContentInBrazilAndMexico: FC = () => {
return (
<LocaleGate countries={[CountryCode.Brazil, CountryCode.Mexico]}>
{/*Brazil & Mexico only content here*/}
</LocaleGate>
);
};
By default <LocaleGate>
will fall back to rendering null
. You can
optionally pass a fallback.
Operating as a Allowlist with Fallback
import type { FC } from 'react';
import { LocaleGate, CountryCode } from 'tachyon-intl';
const FallBackComponent: FC = () => {
return (
<div>
This is only available in Brazil & Mexico and you sadly aren't there.
</div>
);
};
const ComponentThatShouldOnlyRenderContentInBrazilAndMexico: FC = () => {
return (
<LocaleGate
countries={[CountryCode.Brazil, CountryCode.Mexico]}
fallback={FallBackComponent} // you can also write this as () => <FallbackComponent />
>
{/* Brazil & Mexico only content here */}
</LocaleGate>
);
};
Operating as a Blocklist
import type { FC } from 'react';
import { LocaleGate, CountryCode } from 'tachyon-intl';
const ComponentThatShouldOnlyRenderContentInBrazilAndMexico: FC = () => {
return (
<LocaleGate blocklist countries={[CountryCode.Brazil, CountryCode.Mexico]}>
{/* Globally accessible content EXCEPT for Brazil & Mexico */}
</LocaleGate>
);
};
Warnings
- It's very easy to run into surprising issues by rendering content on the
server that you assumed would happen on the client. Particularly, the
formatDate
andformatTime
methods will probably not behave as you expect when running in a Server Side Rendered application. For mitigation and more details see dates. - When using
<LocaleGate>
be aware that this uses the user's device locale as a trigger. The locale is in the user's control on their device (they can change it by changing their device country & language setting), so do not use this for sensitive features!
Common Issues
You are required to have a valid mwinit
cookie to run intl:download
and
intl:submit
commands. If you aren't you'll see something like:
Downloading latest translated messages...
There was an error downloading the messages.
Error getting strings for project tachyon. Midway/ApiGateway response: 403 Forbidden
Have you run mwinit recently?
Done, with errors. Using --debug may help diagnose issues.