Fbtee
The JavaScript & React Internationalization Framework.
Install / Use
/learn @nkzw-tech/FbteeREADME
fbtee
fbtee (Far Better Translations, Extended Edition) is an internationalization framework for JavaScript & React designed to be powerful, flexible, and intuitive.
fbtee features inline translations and supports dynamic content, pluralization, lists, and more. It is built on top of Facebook's fbt library, which has been used in production for over a decade, serving billions of users.
const Greeting = ({ name }) => (
<div>
<fbt desc="Greeting">
Hello, <Name name={name} />!
</fbt>
</div>
);
Why choose fbtee?
- Inline translations for Better Developer Experience: Embed translations directly into your code. No need to manage translation keys or wrap your code with
t()functions. fbtee uses a compiler to extract strings from your code and prepare them for translation providers. - Proven in Production: Built on Facebook's
fbt, with over a decade of production usage, serving billions of users and two years of production usage in Athena Crisis. - Optimized Performance with IR: Compiles translations into an Intermediate Representation (IR) for extracting strings, then optimizes the runtime output for performance.
- Easy Setup: Quick integration with tools like Babel and Vite means you can get started instantly.
Getting Started
Tired of setting up new projects? Check out these templates for web and React Native that come with fbtee pre-configured:
Examples
Sometimes it's easiest to learn by example and copy-paste the setup from existing projects. Here are some examples of using fbtee:
- Next.js App fbtee Example - A Next.js App Router example using fbtee with Server Components.
- Athena Crisis - an open source video game using fbtee
Installation
fbtee requires at least Node 22, and React 19 if you are using React.
npm install fbtee
In addition to fbtee, you need to install the Babel preset.
npm install -D @nkzw/babel-preset-fbtee
Tooling Setup
If you are using Vite, install the React plugin for Vite:
npm install -D @vitejs/plugin-react
In your vite.config.ts:
import fbteePreset from '@nkzw/babel-preset-fbtee';
import react from '@vitejs/plugin-react';
export default {
plugins: [
react({
babel: {
presets: [fbteePreset],
},
}),
],
};
Using Babel directly instead of Vite's React Plugin
If you are not using @vitejs/plugin-react, for example, because you are using the latest version of React Router in framework mode, you can use vite-plugin-babel instead:
import fbteePreset from '@nkzw/babel-preset-fbtee';
import { reactRouter } from '@react-router/dev/vite';
import { defineConfig } from 'vite';
import babel from 'vite-plugin-babel';
export default defineConfig({
plugins: [
babel({
babelConfig: { presets: [fbteePreset] },
}),
reactRouter(),
],
});
Using fbtee with Next.js
Create a babel.config.js file in the root of your Next.js project and add the fbtee preset:
export default {
presets: ['next/babel', '@nkzw/babel-preset-fbtee'],
};
Scripts
fbtee uses two scripts to manage translations. These scripts help automate the process of collecting, creating, and compiling translations.
fbtee collect: This command reads your source files and extracts all the translatable strings into asource_strings.jsonfile. This file should be uploaded to your translation provider as the source for your translations.fbtee translate: This command takes the collected strings and your translations, and compiles them into an optimized format for use in your application.
By default, fbtee expects your source code to be anywhere within your project, your translations in a translations folder, and the generated translations in src/translations. You can customize these paths using command line arguments:
- The
--translationsparameter can be specified tofbtee translateto customize the path to the input translation files. - The
--output-dirparameter defines where the output translation files per language should be written (one file per locale), so they can be lazy loaded in your app. - The
--output-fileparameter outputs all translations in a single combined file, which can be used directly in your app without loading individual files.
If you want to use different paths, it is recommended to define custom commands in your package.json:
{
"scripts": {
"fbtee:collect": "fbtee collect --src src",
"fbtee:translate": "fbtee translate --translations translations/*.json -o src/translations/"
}
}
Now, run the collect command to set up the initial strings for translation:
pnpm fbtee collect
Add the files generated by these commands to your .gitignore:
.enum_manifest.json
source_strings.json
src/translations/
Because no translations exist yet, you can run this command to create an empty file for now:
mkdir -p translations
echo '{"fb-locale": "ja_JP", "translations": {}}' > translations/ja_JP.json
If you are not using a translation provider, you can also run fbtee prepare-translations --locales ja_JP to generate a JSON file that can be edited directly.
App Setup
fbtee's runtime can manage the currently selected locale for you through the <LocaleContext /> component. All you need to do is define the available languages, the locales provided by the browser or device, and a function to load translations for a given locale:
import { getLocales } from 'expo-localization';
import { createLocaleContext } from 'fbtee';
// Define the available languages in your app:
const availableLanguages = new Map([
['en_US', 'English'],
['ja_JP', '日本語 (Japanese)'],
]);
// Web:
const clientLocalesWeb = [navigator.language, ...navigator.languages];
// React Native:
const clientLocalesRN = getLocales().map(({ languageTag }) => languageTag);
// A loader function to fetch translations for a given locale:
const loadLocale = async (locale: string) => {
if (locale === 'ja_JP') {
return (await import('./translations/ja_JP.json')).default.ja_JP;
}
return {};
};
const LocaleContext = createLocaleContext({
availableLanguages,
clientLocales: clientLocalesWeb, // or clientLocalesRN for React Native
loadLocale,
});
// Now wrap your app with `LocaleContext`:
const MyAppEntryPoint = () => (
<LocaleContext>
<App />
</LocaleContext>
);
If you need to access the current locale or set the locale, you can use the useLocaleContext hook:
import { useLocaleContext } from 'fbtee';
const MyComponent = () => {
const { locale, setLocale } = useLocaleContext();
return (
<div>
<p>Current Locale: {locale}</p>
<button onClick={() => setLocale('ja_JP')}>Switch to Japanese</button>
</div>
);
};
Next up, if you are using React and TypeScript in your project, you need to add TypeScript types for fbtee to enable proper type checking in JSX. You can do this by referencing the ReactTypes.d.ts file in your main index.tsx file or a global type declaration file (e.g., types.d.ts):
/// <reference types="fbtee/ReactTypes.d.ts" />
You’re now ready to define your first translatable element!
Alternative Setup Methods
If you need to build your own locale context with custom functionality, you can use the setupLocaleContext function. It takes the same arguments as the LocaleContext component, but gives you full control over how you manage locales.
const { getLocale, setLocale } = setupLocaleContext({
availableLanguages: AvailableLanguages,
clientLocales,
fallbackLocale,
hooks,
loadLocale,
translations,
});
// Now you can call `getLocale` and `setLocale` anytime, even outside of React components.
if (getLocale() === 'en_US') {
setLocale('ja_JP'); // Switch to Japanese locale
}
A full example of using setupLocaleContext to build your own LocaleContext abstraction can be found in the Athena Crisis repository.
Gender Variations
createLocaleContext and setupLocaleContext also support setting the user's gender:
createLocaleContext({
…
gender: 'female', // 'male', 'female' or 'unknown' are supported.
});
If you need to adjust the user's gender dynamically, you can use the setGender function provided by the useLocaleContext hook:
import { useLocaleContext } from 'fbtee';
const GenderSelector = () => {
const { gender, setGender } = useLocaleContext();
return (
<div>
<button onClick={() => setGender('male')}>
<fbt desc="Male gender">Male</fbt>
</button>
<button onClick={() => setGender('female')}>
<fbt desc="Female gender">Female</fbt>
</button>
<button onClick={() => setGender('unknown')}>
<fbt desc="Unknown gender">Unknown</fbt>
</button>
</div>
);
};
Full Customization
Finally, if you are using something other than React, or need even more control over how fbtee is configured, you can use the setupFbtee function. This function allows you to set up fbtee with custom hooks and translations:
import { IntlVariations, setupFbtee } from 'fbtee';
import translations from './translations.json';
setupFbtee({
hooks: {
getViewerContext: () => ({
GENDER: IntlVariations.GENDER_UNKNOWN,
locale: 'en_US',
}),
},
translations,
});
Usage with Next.js
If you are using Next.js with App Router, check out the [Next.js App fbtee Example](https://github.com/cpojer/nextjs-
