From 6f59593a48f4ca69d60dfd7d398b925a0d65665b Mon Sep 17 00:00:00 2001 From: cpojer Date: Thu, 12 Dec 2024 13:35:39 +0900 Subject: [PATCH] README improvements. --- README.md | 111 ++++++++++++++++++ example/src/example/Example.react.tsx | 19 +-- packages/fbtee/{src => }/ReactTypes.d.ts | 0 packages/fbtee/package.json | 2 +- packages/fbtee/src/__tests__/fbs-test.tsx | 6 +- .../fbtee/src/__tests__/fbt-runtime-test.tsx | 4 +- packages/fbtee/src/__tests__/fbt-test.tsx | 6 +- .../fbtee/src/__tests__/intlList-test.tsx | 4 +- .../fbtee/src/__tests__/intlNumUtils-test.tsx | 4 +- .../fbtee/src/__tests__/mock-fbt-test.tsx | 4 +- packages/fbtee/src/fbt.tsx | 2 +- packages/fbtee/src/index.tsx | 2 +- packages/fbtee/src/intlList.tsx | 2 +- .../fbtee/src/{init.tsx => setupFbtee.tsx} | 4 +- 14 files changed, 142 insertions(+), 28 deletions(-) rename packages/fbtee/{src => }/ReactTypes.d.ts (100%) rename packages/fbtee/src/{init.tsx => setupFbtee.tsx} (95%) diff --git a/README.md b/README.md index 0fedcb8d..c3480c52 100644 --- a/README.md +++ b/README.md @@ -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 +/// +``` + _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 `` (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 = () =>
Hello, World!
; +``` + +You can wrap the string with ``: + +```tsx +const Greeting = () => ( +
+ Hello, World! +
+); +``` + +`` 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 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 ``: + +```tsx +const Greeting = ({ name }) => ( +
+ + Hello, {name}! + +
+); +``` + +**fbtee** allows you to use regular React Components inside of `` which will automatically create `` calls for you: + +```tsx +const Greeting = ({ name }) => ( +
+ + Hello, ! + +
+); +``` + +_Note: is auto-imported for you by the `@nkzw/babel-fbtee` plugin._ + +After marking your strings for translation with ``, 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. @@ -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**. diff --git a/example/src/example/Example.react.tsx b/example/src/example/Example.react.tsx index 1da6dd78..caa4831c 100644 --- a/example/src/example/Example.react.tsx +++ b/example/src/example/Example.react.tsx @@ -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, }, @@ -73,14 +73,17 @@ export default function Example() { const [ex2Object, setEx2Object] = useState('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) => { diff --git a/packages/fbtee/src/ReactTypes.d.ts b/packages/fbtee/ReactTypes.d.ts similarity index 100% rename from packages/fbtee/src/ReactTypes.d.ts rename to packages/fbtee/ReactTypes.d.ts diff --git a/packages/fbtee/package.json b/packages/fbtee/package.json index 8e35aba1..b823d984 100644 --- a/packages/fbtee/package.json +++ b/packages/fbtee/package.json @@ -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": { diff --git a/packages/fbtee/src/__tests__/fbs-test.tsx b/packages/fbtee/src/__tests__/fbs-test.tsx index 3191baaf..5c52d5d5 100644 --- a/packages/fbtee/src/__tests__/fbs-test.tsx +++ b/packages/fbtee/src/__tests__/fbs-test.tsx @@ -1,12 +1,12 @@ -/// +/// 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, }, diff --git a/packages/fbtee/src/__tests__/fbt-runtime-test.tsx b/packages/fbtee/src/__tests__/fbt-runtime-test.tsx index f793a0af..14038290 100644 --- a/packages/fbtee/src/__tests__/fbt-runtime-test.tsx +++ b/packages/fbtee/src/__tests__/fbt-runtime-test.tsx @@ -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(). @@ -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: {}, }); diff --git a/packages/fbtee/src/__tests__/fbt-test.tsx b/packages/fbtee/src/__tests__/fbt-test.tsx index 59a4c062..1f4dadcd 100644 --- a/packages/fbtee/src/__tests__/fbt-test.tsx +++ b/packages/fbtee/src/__tests__/fbt-test.tsx @@ -1,4 +1,4 @@ -/// +/// import { describe, expect, it, jest } from '@jest/globals'; import { PatternHash } from '@nkzw/babel-plugin-fbtee'; @@ -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: {} }, }); diff --git a/packages/fbtee/src/__tests__/intlList-test.tsx b/packages/fbtee/src/__tests__/intlList-test.tsx index 13a5ae22..37d02e05 100644 --- a/packages/fbtee/src/__tests__/intlList-test.tsx +++ b/packages/fbtee/src/__tests__/intlList-test.tsx @@ -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, diff --git a/packages/fbtee/src/__tests__/intlNumUtils-test.tsx b/packages/fbtee/src/__tests__/intlNumUtils-test.tsx index e51a0c51..27e9c5fc 100644 --- a/packages/fbtee/src/__tests__/intlNumUtils-test.tsx +++ b/packages/fbtee/src/__tests__/intlNumUtils-test.tsx @@ -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: {}, }); diff --git a/packages/fbtee/src/__tests__/mock-fbt-test.tsx b/packages/fbtee/src/__tests__/mock-fbt-test.tsx index 62e7d8e7..2ac13008 100644 --- a/packages/fbtee/src/__tests__/mock-fbt-test.tsx +++ b/packages/fbtee/src/__tests__/mock-fbt-test.tsx @@ -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, diff --git a/packages/fbtee/src/fbt.tsx b/packages/fbtee/src/fbt.tsx index 1d7764b3..200457d7 100644 --- a/packages/fbtee/src/fbt.tsx +++ b/packages/fbtee/src/fbt.tsx @@ -1,4 +1,4 @@ -/// +/// import type { FbtTableKey, PatternString } from '@nkzw/babel-plugin-fbtee'; import invariant from 'invariant'; diff --git a/packages/fbtee/src/index.tsx b/packages/fbtee/src/index.tsx index c58ca577..fa37cc4a 100644 --- a/packages/fbtee/src/index.tsx +++ b/packages/fbtee/src/index.tsx @@ -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'; diff --git a/packages/fbtee/src/intlList.tsx b/packages/fbtee/src/intlList.tsx index 5995e9be..05884a3d 100644 --- a/packages/fbtee/src/intlList.tsx +++ b/packages/fbtee/src/intlList.tsx @@ -2,7 +2,7 @@ * @fbt {"project": "intl-core"} */ -/// +/// import invariant from 'invariant'; import { isValidElement } from 'react'; diff --git a/packages/fbtee/src/init.tsx b/packages/fbtee/src/setupFbtee.tsx similarity index 95% rename from packages/fbtee/src/init.tsx rename to packages/fbtee/src/setupFbtee.tsx index 103adace..ac8ac8b6 100644 --- a/packages/fbtee/src/init.tsx +++ b/packages/fbtee/src/setupFbtee.tsx @@ -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 type { IFbtErrorListener, NestedFbtContentItems } from './Types.d.ts'; +import type { IFbtErrorListener, NestedFbtContentItems } from './Types.js'; const getFbtResult = ( contents: NestedFbtContentItems, @@ -12,7 +12,7 @@ const getFbtResult = ( errorListener: IFbtErrorListener | null, ) => new FbtResult(contents, errorListener, hashKey); -export default function init({ +export default function setupFbtee({ hooks, translations, }: {