Skip to content

Commit

Permalink
Build improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpojer committed Dec 12, 2024
1 parent ccc1ee7 commit 0fceb7c
Show file tree
Hide file tree
Showing 28 changed files with 96 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ example/src/translatedFbts
example/src/translatedFbts.json
node_modules
packages/*/lib
packages/*/LICENSE
packages/*/README.md
packages/fbt/lib
packages/fbt/LICENSE
tsconfig.tsbuildinfo
77 changes: 64 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,74 @@
<h1 align="center">
<img src="https://facebook.github.io/fbt/img/fbt.png" height="150" width="150" alt="fbt"/>
</h1>
# fbtee

**fbtee** (Far Better Translations, Extended Edition) is an internationalization framework for JavaScript designed to be **powerful**, **flexible**, and **intuitive**.
**fbtee** (Far Better Translations, _Extended Edition_) is an internationalization framework for JavaScript & React designed to be **powerful**, **flexible**, and **intuitive**.

It helps with the following:
## Why fbtee?

-

* Organizing your source text for translation
* Composing grammatically correct translatable User Interfaces
* Eliminating verbose boilerplate for generating User Interfaces
- **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 one year of production usage in [Athena Crisis](https://athenacrisis.com).
- **Optimized Performance with IR:** Compiles translations into an Intermediate Representation (IR) for extracting strings, and optimizes the runtime output for performance.
- **Easy Setup:** Quick integration with tools like Babel and Vite means you can get started instantly.

## Status: Ready for Early Adopters

This is a fork of Facebook's original fbt library, which has been archived. The aim of this fork is to create the best and most modern internationalization library for JavaScript & React.
This is a fork of Facebook's original `fbt` library, which has been archived. The aim of this fork is to create the best and most modern internationalization library for JavaScript & React.

## Getting Started

```bash
npm install fbtee @nkzw/babel-fbtee
```

In your `vite.config.ts`:

```ts
import fbteePreset from '@nkzw/babel-fbtee';
import react from '@vitejs/plugin-react';

export default {
plugins: [
react({
babel: {
presets: [fbteePreset],
},
}),
],
};
```

_You’re now ready to go!_

## Usage

## What's better about fbtee than fbt?

Facebook has done an amazing job with `fbt`, an internationalization library that has been successfully used in production at Facebook for over 10 years. Their work provided a strong foundation for modern localization tools.

The open-source version of `fbt`, however, became unmaintained, difficult to set up, and incompatible with modern tools. It was eventually archived in November 2024. **fbtee** builds on this foundation with several improvements:

- **Easier Setup:** fbtee works with modern tools like Vite.
- **Improved React Compatibility:** Removed React-specific hacks and added support for implicit React fragments (`<>`).
- **Enhanced Features:** Fixed and exported `intlList`, which was not functional in the original `fbt`.
- **Modernized Codebase:** Rewritten using TypeScript, ES modules (ESM), eslint, and modern JavaScript standards. Removed cruft and legacy code.
- **Updated Tooling:** Uses modern tools like pnpm, Vite, and esbuild for faster and more efficient development of **fbtee**.

**fbtee** remains compatible with `fbt` and migration is straightforward.

## Migration from `fbt`:

**fbtee** is compatible with `fbt`. If you are already using `fbt`, migrating to fbtee is straightforward:

- Replace `import { fbt } from 'fbt'` with `import { fbt } from 'fbtee'`.
- Rename commands from `fbt-collect`, `fbt-manifest` and `fbt-translate` to `fbtee-collect`, `fbtee-manifest` and `fbtee-translate`.
- If you were using CommonJS modules for common strings or enums, convert them to ES modules.
- Ensure you are using the latest version of Node.js 22 or later.

After these changes, your project should work seamlessly with **fbtee**.

_Note: Some legacy behavior and options were removed from `fbtee`. If you have a complex setup, please consider [reaching out to us for help](mailto:[email protected])._

## Credits

- `fbt` was originally created by [Facebook](https://github.com/facebook/fbt).
- The auto-import plugin was created by @alexandernanberg.
- [Nakazawa Tech](https://nkzw.tech) rewrote fbt into fbtee and is maintaining this project.
- The auto-import plugin was created by [@alexandernanberg](https://github.com/alexandernanberg).
- [Nakazawa Tech](https://nkzw.tech) rewrote `fbt` into `fbtee` and continues to maintain this project.
File renamed without changes.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"type": "module",
"scripts": {
"build": "pnpm -r build",
"build:all": "pnpm -r build && pnpm -r build:types && pnpm install && cd example && pnpm build:fbtee",
"build:all": "pnpm -r build && pnpm -r build:types && pnpm install && pnpm copy-files && cd example && pnpm build:fbtee",
"copy-files": "find packages/* -type d -maxdepth 0 -exec cp README.md LICENSE {} \\;",
"clean": "rm -rf packages/*/lib; cd example pnpm clean",
"dev": "cd example && pnpm build:fbtee && pnpm dev",
"lint:format": "prettier --cache --check .",
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/JSFbtUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
TableJSFBTTreeLeaf,
} from './index.tsx';
import nullthrows from './nullthrows.tsx';
import type { FbtTableKey } from './Types.tsx';
import type { FbtTableKey } from './Types.d.ts';

export function isTableJSFBTTreeLeaf(
value: Partial<TableJSFBTTreeLeaf>,
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/bin/FbtCollector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import fbt, {
getExtractedStrings,
getFbtElementNodes,
} from '../index.tsx';
import type { PatternHash, PatternString } from '../Types.tsx';
import type { PatternHash, PatternString } from '../Types.d.ts';

export type ExternalTransform = (
src: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/bin/TextPackager.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { onEachLeaf } from '../JSFbtUtil.tsx';
import type { PatternHash, PatternString } from '../Types.tsx';
import type { PatternHash, PatternString } from '../Types.d.ts';
import type { HashToLeaf, PackagerPhrase } from './FbtCollector.tsx';

export type HashFunction = (
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/bin/translateUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import TranslationBuilder from '../translate/TranslationBuilder.tsx';
import TranslationConfig from '../translate/TranslationConfig.tsx';
import type { SerializedTranslationData } from '../translate/TranslationData.tsx';
import TranslationData from '../translate/TranslationData.tsx';
import type { PatternHash, PatternString } from '../Types.tsx';
import type { PatternHash, PatternString } from '../Types.d.ts';
import type { CollectFbtOutput, CollectFbtOutputPhrase } from './collect.tsx';

export type Options = Readonly<{
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import FbtEnumRegistrar from './FbtEnumRegistrar.tsx';
import FbtNodeChecker from './FbtNodeChecker.tsx';
import { checkOption, errorAt } from './FbtUtil.tsx';
import { FbtVariationType } from './translate/IntlVariations.tsx';
import { FbtTableKey, PatternHash, PatternString } from './Types.tsx';
import type { FbtTableKey, PatternHash, PatternString } from './Types.d.ts';

export { SENTINEL } from './FbtConstants.tsx';
export { default as fbtHashKey } from './fbtHashKey.tsx';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { tokenNameToTextPattern } from './fbt-nodes/FbtNodeUtil.tsx';
import { TokenAliases } from './index.tsx';
import { PatternString } from './Types.tsx';
import type { PatternString } from './Types.d.ts';

/**
* Clear token names in translations and runtime call texts need to be replaced
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/translate/FbtSite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
} from '../index.tsx';
import { isTableJSFBTTreeLeaf, onEachLeaf } from '../JSFbtUtil.tsx';
import nullthrows from '../nullthrows.tsx';
import type { PatternHash, PatternString } from '../Types.tsx';
import type { PatternHash, PatternString } from '../Types.d.ts';
import type {
FbtSiteHashifiedTableJSFBTTree,
FbtSiteHashToTextAndDesc,
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-plugin-fbtee/src/translate/FbtSiteBase.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HashToLeaf } from '../bin/FbtCollector.tsx';
import type { FbtTableKey, PatternHash, PatternString } from '../Types.tsx';
import type { FbtTableKey, PatternHash, PatternString } from '../Types.d.ts';
import type {
IntlFbtVariationTypeValue,
IntlVariationMaskValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import invariant from 'invariant';
import { varDump } from '../FbtUtil.tsx';
import nullthrows from '../nullthrows.tsx';
import replaceClearTokensWithTokenAliases from '../replaceClearTokensWithTokenAliases.tsx';
import type { FbtTableKey, PatternHash } from '../Types.tsx';
import type { FbtTableKey, PatternHash } from '../Types.d.ts';
import { FbtSite, FbtSiteMetaEntry } from './FbtSite.tsx';
import type { FbtSiteHashifiedTableJSFBTTree } from './FbtSiteBase.tsx';
import type {
Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/FbtResult.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import FbtResultBase from './FbtResultBase.tsx';
import {
import type {
BaseResult,
IFbtErrorListener,
NestedFbtContentItems,
} from './Types.tsx';
} from './Types.d.ts';

type Props = Readonly<{
content: NestedFbtContentItems;
Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/FbtResultBase.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
import type {
BaseResult,
FbtContentItem,
IFbtErrorListener,
NestedFbtContentItems,
} from './Types.tsx';
} from './Types.d.ts';

export default class FbtResultBase implements BaseResult {
_contents: NestedFbtContentItems;
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/FbtTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FbtTableKey } from '@nkzw/babel-plugin-fbtee/src/Types.tsx';
import type { FbtTableKey } from '@nkzw/babel-plugin-fbtee/src/Types.d.ts';
import invariant from 'invariant';
import type { FbtRuntimeInput, FbtTableArgs } from './Hooks.tsx';

Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/Hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import type {
import FbtResult from './FbtResult.tsx';
import type { FbtTableArg } from './FbtTableAccessor.tsx';
import IntlViewerContext from './IntlViewerContext.tsx';
import {
import type {
BaseResult,
FbtErrorContext,
IFbtErrorListener,
NestedFbtContentItems,
PureStringResult,
} from './Types.tsx';
} from './Types.d.ts';

export type ResolverFn<T extends BaseResult> = (
contents: NestedFbtContentItems,
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/fbtee/src/__mocks__/getFbtResult.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FbtResult from '../FbtResult.tsx';
import { NestedFbtContentItems } from '../Types.tsx';
import type { NestedFbtContentItems } from '../Types.d.ts';

export default function getFbtResult(
contents: NestedFbtContentItems,
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/__tests__/FbtResult-test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it, jest } from '@jest/globals';
import FbtResult from '../FbtResult.tsx';
import Hooks from '../Hooks.tsx';
import { IFbtErrorListener } from '../Types.tsx';
import type { IFbtErrorListener } from '../Types.d.ts';

let errorListener: IFbtErrorListener | null;

Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/__tests__/fbt-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import Hooks, { FbtRuntimeCallInput, FbtTranslatedInput } from '../Hooks.tsx';
import { fbt, FbtResult } from '../index.tsx';
import init from '../init.tsx';
import IntlVariations from '../IntlVariations.tsx';
import {
import type {
BaseResult,
IFbtErrorListener,
NestedFbtContentItems,
} from '../Types.tsx';
} from '../Types.d.ts';

init({
translations: { en_US: {} },
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/fbt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
getNumberVariations,
} from './IntlVariationResolver.tsx';
import substituteTokens, { Substitutions } from './substituteTokens.tsx';
import { BaseResult, NestedFbtContentItems } from './Types.tsx';
import type { BaseResult, NestedFbtContentItems } from './Types.d.ts';

const ParamVariation: ParamVariationType = {
gender: 1,
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/getAllSubstitutions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import invariant from 'invariant';
import FbtTable from './FbtTable.tsx';
import { FbtTableArgs } from './Hooks.tsx';
import { Substitutions } from './substituteTokens.tsx';
import { FbtContentItem } from './Types.tsx';
import type { FbtContentItem } from './Types.d.ts';

export default function getAllSubstitutions(args: FbtTableArgs) {
let substitutions: Substitutions | null = null;
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/getFbsResult.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PatternHash } from '@nkzw/babel-plugin-fbtee';
import FbtPureStringResult from './FbtPureStringResult.tsx';
import { IFbtErrorListener, NestedFbtContentItems } from './Types.tsx';
import type { IFbtErrorListener, NestedFbtContentItems } from './Types.d.ts';

export default function getFbsResult(
contents: NestedFbtContentItems,
Expand Down
4 changes: 3 additions & 1 deletion packages/fbtee/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import fbsInternal from './fbs.tsx';
import fbtInternal from './fbt.tsx';
import { FbsAPI, FbtAPI } from './Types.tsx';
import type { FbsAPI, FbtAPI } from './Types.d.ts';

export { default as IntlVariations } from './IntlVariations.tsx';
export { default as init } from './init.tsx';
export { default as GenderConst } from './GenderConst.tsx';
export { default as FbtTranslations } from './FbtTranslations.tsx';
export { default as FbtResult } from './FbtResult.tsx';
export { default as intlList } from './intlList.tsx';

export const fbt = fbtInternal as unknown as FbtAPI;
export const fbs = fbsInternal as unknown as FbsAPI;
2 changes: 1 addition & 1 deletion packages/fbtee/src/init.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import FbtTranslations, { TranslationDict } from './FbtTranslations.tsx';
import getFbsResult from './getFbsResult.tsx';
import Hook, { Hooks } from './Hooks.tsx';
import IntlViewerContext from './IntlViewerContext.tsx';
import { IFbtErrorListener, NestedFbtContentItems } from './Types.tsx';
import type { IFbtErrorListener, NestedFbtContentItems } from './Types.d.ts';

const getFbtResult = (
contents: NestedFbtContentItems,
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/substituteTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
dedupeStops,
PUNCT_CHAR_CLASS,
} from './IntlPunctuation.tsx';
import { FbtContentItem, NestedFbtContentItems } from './Types.tsx';
import type { FbtContentItem, NestedFbtContentItems } from './Types.d.ts';

// This pattern finds tokens inside a string: 'string with {token} inside'.
// It also grabs any punctuation that may be present after the token, such as
Expand Down

0 comments on commit 0fceb7c

Please sign in to comment.