@nkzw/eslint-plugin-fbtee 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,9 +4,9 @@ _**fbtee** (Far Better Translations, Extended Edition) is an internationalizatio
4
4
 
5
5
  ## Why `fbtee`?
6
6
 
7
- - **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.
8
- - **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).
9
- - **Optimized Performance with IR:** Compiles translations into an Intermediate Representation (IR) for extracting strings, and optimizes the runtime output for performance.
7
+ - **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.
8
+ - **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](https://athenacrisis.com).
9
+ - **Optimized Performance with IR:** Compiles translations into an Intermediate Representation (IR) for extracting strings, then optimizes the runtime output for performance.
10
10
  - **Easy Setup:** Quick integration with tools like Babel and Vite means you can get started instantly.
11
11
 
12
12
  ## Status: Ready for Early Adopters
@@ -15,6 +15,13 @@ This is a fork of Facebook's original `fbt` library, which has been archived. Th
15
15
 
16
16
  ## Getting Started
17
17
 
18
+ Tired of setting up new projects? Check out these templates for web and React Native that come with **fbtee** pre-configured:
19
+
20
+ - [Web App Template](https://github.com/nkzw-tech/web-app-template)
21
+ - [Expo App Template](https://github.com/nkzw-tech/expo-app-template)
22
+
23
+ ### Installation
24
+
18
25
  **fbtee** requires at least Node 22, and React 19 if you are using React.
19
26
 
20
27
  ```bash
@@ -27,6 +34,8 @@ In addition to `fbtee`, you need to install the Babel preset and React plugin fo
27
34
  npm install -D @nkzw/babel-preset-fbtee @vitejs/plugin-react
28
35
  ```
29
36
 
37
+ ### Tooling Setup
38
+
30
39
  In your `vite.config.ts`:
31
40
 
32
41
  ```ts
@@ -44,40 +53,164 @@ export default {
44
53
  };
45
54
  ```
46
55
 
47
- **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`:
56
+ #### Using Babel directly instead of Vite's React Plugin
57
+
58
+ 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:
59
+
60
+ ```tsx
61
+ import fbteePreset from '@nkzw/babel-preset-fbtee';
62
+ import { reactRouter } from '@react-router/dev/vite';
63
+ import { defineConfig } from 'vite';
64
+ import babel from 'vite-plugin-babel';
65
+
66
+ export default defineConfig({
67
+ plugins: [
68
+ babel({
69
+ babelConfig: { presets: [fbteePreset] },
70
+ }),
71
+ reactRouter(),
72
+ ],
73
+ });
74
+ ```
75
+
76
+ #### Scripts
77
+
78
+ **fbtee** uses two scripts to manage translations. These scripts help automate the process of collecting, creating, and compiling translations.
79
+
80
+ - `fbtee collect`: This command reads your source files and extracts all the translatable strings into a `source_strings.json` file. This file should be uploaded to your translation provider as the source for your translations.
81
+ - `fbtee translate`: This command takes the collected strings and your translations, and compiles them into an optimized format for use in your application.
82
+
83
+ 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:
84
+
85
+ - The `--translations` parameter can be specified to `fbtee translate` to customize the path to the input translation files.
86
+ - `--output-dir` can be specified to define where the output translations should be written so they can be loaded in your app. You can also use `--out` parameter to output a single file such as `translations.json`, which can be used directly in your app without loading individual translation files.
87
+
88
+ If you want to use different paths, it is recommended to define custom commands in your `package.json`:
48
89
 
49
90
  ```json
50
91
  {
51
92
  "scripts": {
52
- "fbtee:manifest": "fbtee manifest --src src",
53
- "fbtee:collect": "fbtee collect --manifest < .src_manifest.json > source_strings.json",
54
- "fbtee:translate": "fbtee translate --translations translations/*.json > src/translations.json"
93
+ "fbtee:collect": "fbtee collect --src src",
94
+ "fbtee:translate": "fbtee translate --translations translations/*.json -o src/translations/"
55
95
  }
56
96
  }
57
97
  ```
58
98
 
59
- Here is what each command does:
60
-
61
- - `fbtee manifest --src src` searches through files in the `src` directory and generates a manifest file (`.src_manifest.json`) that lists all the files translatable strings marked with `<fbt>`. In addition it creates `.enum_manifest.json` which lists all the files containing `<fbt:enum>` tags. Since these files are auto-generated, we recommend adding them to your `.gitignore`.
62
- - `fbtee collect --manifest < .src_manifest.json > source_strings.json` reads the manifest file and extracts all the strings marked for translation into a `source_strings.json` file. This file should be uploaded to your translation provider as the source for your translations.
63
- - `fbtee translate --translations translations/*.json --jenkins > src/translations.json` reads all the translations in the `translations/` directory and compiles them into a single `src/translations.json` file. This file is used by **fbtee** to display translated content in your app. The `--translations` parameter specifies the path to the translation files. The `--jenkins` flag is used to define the utilized [hash function](https://en.wikipedia.org/wiki/Jenkins_hash_function). You can adjust the `--translations` parameter to point to your translation directory. If your translations are within your app directory in a folder called `i18n`, for example, you'd use `--translations app/i18n/*.json`. The output is the translation file used by **fbtee**. This file is going to be referred to in your applications entrypoint, therefore it needs to be generated as part of the build, but should not be part of version control. This command requires the translations to be present in the `translations/` directory, created based on real translations of what was previously collected using the `collect` command. Instead of outputting a single file, you can also specify `--output-dir` instead.
64
-
65
- Now, the first step is to run these commands to set up the initial strings for translation:
99
+ Now, run the collect command to set up the initial strings for translation:
66
100
 
67
101
  ```bash
68
- npm run fbtee:manifest && npm run fbtee:collect
102
+ pnpm fbtee collect
69
103
  ```
70
104
 
71
105
  The files generated by these commands should be added to `.gitignore`:
72
106
 
73
107
  ```
74
- .src_manifest.json
75
108
  .enum_manifest.json
76
109
  source_strings.json
77
- src/translations.json
110
+ src/translations/
78
111
  ```
79
112
 
80
- Next, set up **fbtee** in your app's initialization code (e.g., `src/index.tsx`):
113
+ Since you won't have any translations yet, we need to create an empty translations file. Just run this command to create the empty file:
114
+
115
+ ```bash
116
+ mkdir -p translations
117
+ echo '{"fb-locale": "ja_JP", "translations": {}}' > translations/ja_JP.json
118
+ ```
119
+
120
+ If you are not using a translation provider, you can also run `fbtee prepare-translations --locale ja_JP` to generate a JSON file that can be edited directly.
121
+
122
+ ### App Setup
123
+
124
+ **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:
125
+
126
+ ```tsx
127
+ import { getLocales } from 'expo-localization';
128
+ import { createLocaleContext } from 'fbtee';
129
+
130
+ // Define the available languages in your app:
131
+ const availableLanguages = new Map([
132
+ ['en_US', 'English'],
133
+ ['ja_JP', '日本語 (Japanese)'],
134
+ ]);
135
+
136
+ // Web:
137
+ const clientLocalesWeb = [navigator.language, ...navigator.languages];
138
+ // React Native:
139
+ const clientLocalesRN = getLocales().map(({ languageTag }) => languageTag);
140
+
141
+ // A loader function to fetch translations for a given locale:
142
+ const loadLocale = async (locale: string) => {
143
+ if (locale === 'ja_JP') {
144
+ return (await import('./translations/ja_JP.json')).default.ja_JP;
145
+ }
146
+
147
+ return {};
148
+ };
149
+
150
+ const LocaleContext = createLocaleContext({
151
+ availableLangauges,
152
+ clientLocales: clientLocalesWeb, // or clientLocalesRN for React Native
153
+ loadLocale,
154
+ });
155
+
156
+ // Now wrap your app with `LocaleContext`:
157
+ const MyAppEntryPoint = () => (
158
+ <LocaleContext>
159
+ <App />
160
+ </LocaleContext>
161
+ );
162
+ ```
163
+
164
+ If you need to access the current locale or set the locale, you can use the `useLocaleContext` hook:
165
+
166
+ ```tsx
167
+ import { useLocaleContext } from 'fbtee';
168
+
169
+ const MyComponent = () => {
170
+ const { locale, setLocale } = useLocaleContext();
171
+
172
+ return (
173
+ <div>
174
+ <p>Current Locale: {locale}</p>
175
+ <button onClick={() => setLocale('ja_JP')}>Switch to Japanese</button>
176
+ </div>
177
+ );
178
+ };
179
+ ```
180
+
181
+ 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`):
182
+
183
+ ```tsx
184
+ /// <reference types="fbtee/ReactTypes.d.ts" />
185
+ ```
186
+
187
+ _You’re now ready to define your first translatable element!_
188
+
189
+ ### Alternative Setup Methods
190
+
191
+ 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.
192
+
193
+ ```tsx
194
+ const { getLocale, setLocale } = setupLocaleContext({
195
+ availableLanguages: AvailableLanguages,
196
+ clientLocales,
197
+ fallbackLocale,
198
+ hooks,
199
+ loadLocale,
200
+ translations,
201
+ });
202
+
203
+ // Now you can call `getLocale` and `setLocale` anytime, even outside of React components.
204
+ if (getLocale() === 'en_US') {
205
+ setLocale('ja_JP'); // Switch to Japanese locale
206
+ }
207
+ ```
208
+
209
+ A full example of using `setupLocaleContext` to build your own `LocaleContext` abstraction can be found in the [Athena Crisis](https://github.com/nkzw-tech/athena-crisis/blob/main/hera/i18n/LocaleContext.tsx#L41) repository.
210
+
211
+ #### Full Customization
212
+
213
+ 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:
81
214
 
82
215
  ```tsx
83
216
  import { IntlVariations, setupFbtee } from 'fbtee';
@@ -94,19 +227,11 @@ setupFbtee({
94
227
  });
95
228
  ```
96
229
 
97
- 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`):
98
-
99
- ```tsx
100
- /// <reference types="fbtee/ReactTypes.d.ts" />
101
- ```
102
-
103
- _You’re now ready to define your first translatable element!_
104
-
105
230
  ## Usage
106
231
 
107
- _If you want to learn by example, check out the [examples](https://github.com/nkzw-tech/fbtee/tree/main/example) directory._
232
+ _If you want to learn by example, check out the [examples](https://github.com/nkzw-tech/fbtee/tree/main/example) directory. You can also check out [Athena Crisis](https://github.com/nkzw-tech/athena-crisis), a large open source video game using **fbtee**_
108
233
 
109
- 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.
234
+ With **fbtee**, all strings need to be wrapped using `<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.
110
235
 
111
236
  Here are some basic examples:
112
237
 
@@ -153,7 +278,7 @@ _Note: `<fbt>` is auto-imported for you by the `@nkzw/babel-preset-fbtee` plugin
153
278
  After marking your strings for translation with `<fbt>`, run the following commands to extract, and compile translations:
154
279
 
155
280
  ```bash
156
- npm run fbtee:manifest && npm run fbtee:collect
281
+ pnpm fbtee collect
157
282
  ```
158
283
 
159
284
  You can now upload the `source_strings.json` file to your translation provider. One sample for such a translation provider is [Crowdin](https://crowdin.com/).
@@ -234,7 +359,7 @@ And German (`translations/de_DE.json`) might look like this:
234
359
  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:
235
360
 
236
361
  ```bash
237
- npm run fbtee:translate
362
+ pnpm fbtee translate
238
363
  ```
239
364
 
240
365
  This will generate the `translation.json` file referred to in the setup done before. This file might look like this:
@@ -248,7 +373,7 @@ This will generate the `translation.json` file referred to in the setup done bef
248
373
 
249
374
  After generating the translations file, your app is ready to display translated content in other languages. The error you had until now in the `setupFbtee` with regards to the imported translations file should be resolved now.
250
375
 
251
- Since the `translations.json` is an auto-generated file part of your build process, the `fbtee:translate` command should be added to your build step.
376
+ Since the `translations.json` is an auto-generated file part of your build process, the `fbtee translate` command should be added to your build step.
252
377
 
253
378
  ## ESLint Plugin
254
379
 
@@ -296,9 +421,9 @@ Facebook has done an amazing job with `fbt`, an internationalization library tha
296
421
  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:
297
422
 
298
423
  - **Easier Setup:** fbtee works with modern tools like Vite.
299
- - **Statically Typed:** The fbtee compiler ensures correct usage of fbtee, libary TypeScript types are provided, and an eslint plugin helps fix common mistakes.
424
+ - **Statically Typed:** The fbtee compiler ensures correct usage of fbtee, library TypeScript types are provided, and an eslint plugin helps fix common mistakes.
300
425
  - **Improved React Compatibility:** Removed React-specific hacks and added support for implicit React fragments (`<>`).
301
- - **Enhanced Features:** Fixed and exported `inltList` as a new `<fbt:list>` construt, which was not functional in the original `fbt`.
426
+ - **Enhanced Features:** Fixed and exported `inltList` as a new `<fbt:list>` construct, which was not functional in the original `fbt`.
302
427
  - **Modernized Codebase:** Rewritten using TypeScript, ES modules (ESM), eslint, and modern JavaScript standards. Removed cruft and legacy code.
303
428
  - **Updated Tooling:** Uses modern tools like pnpm, Vite, and esbuild for faster and more efficient development of **fbtee**.
304
429
 
@@ -311,7 +436,7 @@ The open-source version of `fbt`, however, became unmaintained, difficult to set
311
436
  - Follow the "Getting Started" guide above and remove all "fbt" related packages.
312
437
  - Make sure you are using React 19.
313
438
  - Replace `import { fbt } from 'fbt'` with `import { fbt } from 'fbtee'`.
314
- - Rename commands from `fbt-collect`, `fbt-manifest` and `fbt-translate` to `fbtee-collect`, `fbtee-manifest` and `fbtee-translate`.
439
+ - Rename commands from `fbt-collect`, and `fbt-translate` to `fbtee-collect`, and `fbtee-translate`. `fbt manifest` was removed and `fbtee collect` now handles this step automatically.
315
440
  - If you were using CommonJS modules for common strings or enums, convert them to ES modules.
316
441
  - Ensure you are using the latest version of Node.js 22 or later.
317
442
  - Rename your `init({})` call to `setupFbtee({})`.
package/lib/index.cjs CHANGED
@@ -25,7 +25,7 @@ const __typescript_eslint_utils = __toESM(require("@typescript-eslint/utils"));
25
25
 
26
26
  //#region package.json
27
27
  var name = "@nkzw/eslint-plugin-fbtee";
28
- var version = "1.0.0";
28
+ var version = "1.2.0";
29
29
  var description = "";
30
30
  var keywords = [];
31
31
  var repository = {
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { ESLintUtils } from "@typescript-eslint/utils";
2
2
 
3
3
  //#region package.json
4
4
  var name = "@nkzw/eslint-plugin-fbtee";
5
- var version = "1.0.0";
5
+ var version = "1.2.0";
6
6
  var description = "";
7
7
  var keywords = [];
8
8
  var repository = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nkzw/eslint-plugin-fbtee",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "repository": {