Skip to content

Commit

Permalink
README improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpojer committed Dec 12, 2024
1 parent 445a82c commit 6f59593
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 28 deletions.
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,120 @@ export default {
};
```

**fbtee** uses three scripts to manage translations. These scripts help automate the process of collecting, creating, and compiling translations. It is recommended to add them to your `package.json`:

```json
{
"scripts": {
"fbtee:collect": "fbtee collect --manifest < .src_manifest.json > .source_strings.json",
"fbtee:manifest": "fbtee manifest --src src",
"fbtee:translate": "fbtee translate --translations translations/*.json --jenkins > src/translations.json"
}
}
```

Run these commands to set up the initial translation files:

```bash
npm run fbtee:manifest && npm run fbtee:collect && npm run fbtee:translate
```

The files generated by these commands are auto-generated and should not be checked into version control. Add the following entries to your `.gitignore`:

```
.src_manifest.json
.source_strings.json
.enum_manifest.json
src/translations.json
```

Next, set up **fbtee** in your app's initialization code (e.g., `src/index.tsx`):

```tsx
import { IntlVariations, setupFbtee } from 'fbtee';
import translations from './translations.json';

setupFbtee({
hooks: {
getViewerContext: () => ({
GENDER: IntlVariations.GENDER_UNKNOWN,
locale: 'en_US',
}),
},
translations,
});
```

Finally, 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`):

```tsx
/// <reference types="fbtee/ReactTypes.d.ts" />
```

_You’re now ready to go!_

## Usage

_If you want to learn by example, check out the [examples](https://github.com/nkzw-tech/fbtee/tree/main/example) directory._

All strings need to be wrapped by `<fbt>` (for React/JSX) or `fbt()` (for JavaScript). This ensures strings can be extracted and translated properly. The `desc` attribute is required and provides context for translators, helping them understand the intended meaning of the string.

Here are some basic examples:

```tsx
const Greeting = () => <div>Hello, World!</div>;
```

You can wrap the string with `<fbt>`:

```tsx
const Greeting = () => (
<div>
<fbt desc="Greeting">Hello, World!</fbt>
</div>
);
```

`<fbt>` is a special React component that marks text for translation. The `fbtee` compiler analyzes them to extract strings, and compiles them into an Intermediate Representation (IR). It supports dynamic content through <fbt:param> or even other React components. For example, if you want to greet a specific user based on a name passed as a prop, you can use `<fbt:param>`:

```tsx
const Greeting = ({ name }) => (
<div>
<fbt desc="Greeting">
Hello, <fbt:param name="name">{name}</fbt:param>!
</fbt>
</div>
);
```

**fbtee** allows you to use regular React Components inside of `<fbt>` which will automatically create `<fbt:param>` calls for you:

```tsx
const Greeting = ({ name }) => (
<div>
<fbt desc="Greeting">
Hello, <Name name={name} />!
</fbt>
</div>
);
```

_Note: <fbt> is auto-imported for you by the `@nkzw/babel-fbtee` plugin._

After marking your strings for translation with `<fbt>`, run the following commands to extract, and compile translations:

```bash
npm run fbtee:collect
```

You can now upload the `.source_strings.json` file to your translation provider. Once you have the translated strings stored in a `translations/` folder as JSON files, you can run the following command to generate the translations file:

```bash
npm run fbtee:translate
```

After generating the translations file, your app is ready to display translated content in other languages.

## 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.
Expand All @@ -62,6 +172,7 @@ The open-source version of `fbt`, however, became unmaintained, difficult to set
- 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.
- Rename your `init({})` call to `setupFbtee({})`.

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

Expand Down
19 changes: 11 additions & 8 deletions example/src/example/Example.react.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import classNames from 'classnames';
import { fbs, fbt, GenderConst, init, IntlVariations } from 'fbtee';
import { fbs, fbt, GenderConst, IntlVariations, setupFbtee } from 'fbtee';
import { ChangeEvent, useCallback, useState } from 'react';
import translations from '../translatedFbts.json' with { type: 'json' };
import ExampleEnum from './Example$FbtEnum.js';

const viewerContext = {
let viewerContext = {
GENDER: IntlVariations.GENDER_UNKNOWN,
locale: 'en_US',
};

init({
setupFbtee({
hooks: {
getViewerContext: () => viewerContext,
},
Expand Down Expand Up @@ -73,14 +73,17 @@ export default function Example() {
const [ex2Object, setEx2Object] = useState<SharedObj>('LINK');
const [ex2Pronoun, setEx2Pronoun] = useState(GenderConst.UNKNOWN_SINGULAR);

const updateLocale = useCallback((newLocale: Locale) => {
viewerContext.locale = newLocale;
setLocale(newLocale);
const updateLocale = useCallback((locale: Locale) => {
viewerContext = {
...viewerContext,
locale,
};
setLocale(locale);
const html = document.getElementsByTagName('html')[0];
if (html != null) {
html.lang = LOCALES[newLocale].bcp47;
html.lang = LOCALES[locale].bcp47;
}
document.body.className = LOCALES[newLocale].rtl ? 'rtl' : 'ltr';
document.body.className = LOCALES[locale].rtl ? 'rtl' : 'ltr';
}, []);

const onSubmit = useCallback((event: ChangeEvent<HTMLFormElement>) => {
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/fbtee/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"type": "module",
"main": "lib/index.js",
"scripts": {
"build": "rm -f lib/index.js; esbuild --target=node22 --platform=node --format=esm --outfile=lib/index.js --jsx=automatic --bundle ./src/index.tsx; cp ./src/ReactTypes.d.ts ./lib/ReactTypes.d.ts",
"build": "rm -f lib/index.js; esbuild --target=node22 --platform=node --format=esm --outfile=lib/index.js --jsx=automatic --bundle ./src/index.tsx;",
"build:types": "dts-bundle-generator -o lib/index.d.ts src/index.tsx"
},
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions packages/fbtee/src/__tests__/fbs-test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/// <reference types="../ReactTypes.d.ts" />
/// <reference types="../../ReactTypes.d.ts" />

import { describe, expect, it } from '@jest/globals';
import React from 'react';
import { fbs } from '../index.tsx';
import init from '../init.tsx';
import IntlViewerContext from '../IntlViewerContext.tsx';
import setupFbtee from '../setupFbtee.tsx';

init({
setupFbtee({
hooks: {
getViewerContext: () => IntlViewerContext,
},
Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/__tests__/fbt-runtime-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it, jest } from '@jest/globals';
import fbtRuntime from '../fbt.tsx';
import { FbtTableArg } from '../FbtTableAccessor.tsx';
import Hooks, { FbtRuntimeInput } from '../Hooks.tsx';
import { init } from '../index.tsx';
import { setupFbtee } from '../index.tsx';
import intlNumUtils from '../intlNumUtils.tsx';
// Warning: importing JS modules outside of beforeEach blocks is generally bad practice
// in jest tests. We might need to move these modules inside beforeEach().
Expand All @@ -15,7 +15,7 @@ const FEW = String(IntlVariations.NUMBER_FEW);
const MALE = String(IntlVariations.GENDER_MALE);
const FEMALE = String(IntlVariations.GENDER_FEMALE);

init({
setupFbtee({
translations: {},
});

Expand Down
6 changes: 3 additions & 3 deletions packages/fbtee/src/__tests__/fbt-test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference types="../ReactTypes.d.ts" />
/// <reference types="../../ReactTypes.d.ts" />

import { describe, expect, it, jest } from '@jest/globals';
import { PatternHash } from '@nkzw/babel-plugin-fbtee';
Expand All @@ -11,15 +11,15 @@ import FbtTranslations from '../FbtTranslations.tsx';
import GenderConst from '../GenderConst.tsx';
import Hooks, { FbtRuntimeCallInput, FbtTranslatedInput } from '../Hooks.tsx';
import { fbt, FbtResult } from '../index.tsx';
import init from '../init.tsx';
import IntlVariations from '../IntlVariations.tsx';
import setupFbtee from '../setupFbtee.tsx';
import type {
BaseResult,
IFbtErrorListener,
NestedFbtContentItems,
} from '../Types.d.ts';

init({
setupFbtee({
translations: { en_US: {} },
});

Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/__tests__/intlList-test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { describe, expect, it } from '@jest/globals';
import getFbtResult from '../__mocks__/getFbtResult.tsx';
import init from '../init.tsx';
import intlList, { Conjunctions, Delimiters } from '../intlList.tsx';
import IntlViewerContext from '../IntlViewerContext.tsx';
import setupFbtee from '../setupFbtee.tsx';

init({
setupFbtee({
hooks: {
getFbtResult,
getViewerContext: () => IntlViewerContext,
Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/__tests__/intlNumUtils-test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { describe, expect, it, jest } from '@jest/globals';
import init from '../init.tsx';
import intlNumUtils from '../intlNumUtils.tsx';
import NumberFormatConsts, { NumberConfig } from '../NumberFormatConsts.tsx';
import setupFbtee from '../setupFbtee.tsx';

init({
setupFbtee({
translations: {},
});

Expand Down
4 changes: 2 additions & 2 deletions packages/fbtee/src/__tests__/mock-fbt-test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { describe, expect, it } from '@jest/globals';
import getFbtResult from '../__mocks__/getFbtResult.tsx';
import { fbt } from '../index.tsx';
import init from '../init.tsx';
import IntlViewerContext from '../IntlViewerContext.tsx';
import setupFbtee from '../setupFbtee.tsx';

init({
setupFbtee({
hooks: {
getFbtResult,
getViewerContext: () => IntlViewerContext,
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/fbt.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference types="./ReactTypes.d.ts" />
/// <reference types="../ReactTypes.d.ts" />

import type { FbtTableKey, PatternString } from '@nkzw/babel-plugin-fbtee';
import invariant from 'invariant';
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fbtInternal from './fbt.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 setupFbtee } from './setupFbtee.tsx';
export { default as GenderConst } from './GenderConst.tsx';
export { default as FbtTranslations } from './FbtTranslations.tsx';
export { default as FbtResult } from './FbtResult.tsx';
Expand Down
2 changes: 1 addition & 1 deletion packages/fbtee/src/intlList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @fbt {"project": "intl-core"}
*/

/// <reference types="./ReactTypes.d.ts" />
/// <reference types="../ReactTypes.d.ts" />

import invariant from 'invariant';
import { isValidElement } from 'react';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import FbtTranslations, { TranslationDict } from './FbtTranslations.tsx';
import getFbsResult from './getFbsResult.tsx';
import Hook, { Hooks } from './Hooks.tsx';
import IntlViewerContext from './IntlViewerContext.tsx';
import type { IFbtErrorListener, NestedFbtContentItems } from './Types.d.ts';
import type { IFbtErrorListener, NestedFbtContentItems } from './Types.js';

const getFbtResult = (
contents: NestedFbtContentItems,
hashKey: PatternHash | null | undefined,
errorListener: IFbtErrorListener | null,
) => new FbtResult(contents, errorListener, hashKey);

export default function init({
export default function setupFbtee({
hooks,
translations,
}: {
Expand Down

0 comments on commit 6f59593

Please sign in to comment.