@nkzw/eslint-plugin-fbtee 1.2.2 → 1.2.5

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
@@ -2,17 +2,13 @@
2
2
 
3
3
  _**fbtee** (Far Better Translations, Extended Edition) is an internationalization framework for JavaScript & React designed to be **powerful**, **flexible**, and **intuitive**._
4
4
 
5
- ## Why `fbtee`?
5
+ ## Why **fbtee**?
6
6
 
7
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
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
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
- ## Status: Ready for Early Adopters
13
-
14
- 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.
15
-
16
12
  ## Getting Started
17
13
 
18
14
  Tired of setting up new projects? Check out these templates for web and React Native that come with **fbtee** pre-configured:
@@ -20,6 +16,13 @@ Tired of setting up new projects? Check out these templates for web and React Na
20
16
  - [Web App Template](https://github.com/nkzw-tech/web-app-template)
21
17
  - [Expo App Template](https://github.com/nkzw-tech/expo-app-template)
22
18
 
19
+ ### Examples
20
+
21
+ Sometimes it's easiest to learn by example and copy-paste the setup from existing projects. Here are some examples of using **fbtee**:
22
+
23
+ - [Next.js App fbtee Example](https://github.com/cpojer/nextjs-fbtee-example) - A Next.js App Router example using **fbtee** with Server Components.
24
+ - [Athena Crisis](https://github.com/nkzw-tech/athena-crisis) - an open source video game using **fbtee**
25
+
23
26
  ### Installation
24
27
 
25
28
  **fbtee** requires at least Node 22, and React 19 if you are using React.
@@ -28,14 +31,20 @@ Tired of setting up new projects? Check out these templates for web and React Na
28
31
  npm install fbtee
29
32
  ```
30
33
 
31
- In addition to `fbtee`, you need to install the Babel preset and React plugin for Vite:
34
+ In addition to `fbtee`, you need to install the Babel preset.
32
35
 
33
36
  ```bash
34
- npm install -D @nkzw/babel-preset-fbtee @vitejs/plugin-react
37
+ npm install -D @nkzw/babel-preset-fbtee
35
38
  ```
36
39
 
37
40
  ### Tooling Setup
38
41
 
42
+ If you are using Vite, install the React plugin for Vite:
43
+
44
+ ```bash
45
+ npm install -D @vitejs/plugin-react
46
+ ```
47
+
39
48
  In your `vite.config.ts`:
40
49
 
41
50
  ```ts
@@ -55,7 +64,7 @@ export default {
55
64
 
56
65
  #### Using Babel directly instead of Vite's React Plugin
57
66
 
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:
67
+ 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
68
 
60
69
  ```tsx
61
70
  import fbteePreset from '@nkzw/babel-preset-fbtee';
@@ -73,6 +82,16 @@ export default defineConfig({
73
82
  });
74
83
  ```
75
84
 
85
+ #### Using **fbtee** with Next.js
86
+
87
+ Create a `babel.config.js` file in the root of your Next.js project and add the **fbtee** preset:
88
+
89
+ ```tsx
90
+ export default {
91
+ presets: ['next/babel', '@nkzw/babel-preset-fbtee'],
92
+ };
93
+ ```
94
+
76
95
  #### Scripts
77
96
 
78
97
  **fbtee** uses two scripts to manage translations. These scripts help automate the process of collecting, creating, and compiling translations.
@@ -102,7 +121,7 @@ Now, run the collect command to set up the initial strings for translation:
102
121
  pnpm fbtee collect
103
122
  ```
104
123
 
105
- The files generated by these commands should be added to `.gitignore`:
124
+ Add the files generated by these commands to your `.gitignore`:
106
125
 
107
126
  ```
108
127
  .enum_manifest.json
@@ -110,14 +129,14 @@ source_strings.json
110
129
  src/translations/
111
130
  ```
112
131
 
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:
132
+ Because no translations exist yet, you can run this command to create an empty file for now:
114
133
 
115
134
  ```bash
116
135
  mkdir -p translations
117
136
  echo '{"fb-locale": "ja_JP", "translations": {}}' > translations/ja_JP.json
118
137
  ```
119
138
 
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.
139
+ 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.
121
140
 
122
141
  ### App Setup
123
142
 
@@ -148,7 +167,7 @@ const loadLocale = async (locale: string) => {
148
167
  };
149
168
 
150
169
  const LocaleContext = createLocaleContext({
151
- availableLangauges,
170
+ availableLanguages,
152
171
  clientLocales: clientLocalesWeb, // or clientLocalesRN for React Native
153
172
  loadLocale,
154
173
  });
@@ -227,11 +246,19 @@ setupFbtee({
227
246
  });
228
247
  ```
229
248
 
249
+ ### Usage with Next.js
250
+
251
+ If you are using Next.js with App Router, check out the [Next.js App fbtee Example](https://github.com/cpojer/nextjs-fbtee-example). You'll need to set up a `<LocaleContext />` as a Client Component, and call `setupFbtee` in the root layout of your app for Server Component support.
252
+
253
+ When using Next.js with **fbtee**, translated strings are inserted when your Server Components are rendered. This means that changing locales on the client will not automatically update the Server Component's translated strings. To handle locale changes, you need to call a server action that updates the locale, and then re-render your app with the new locale, for example by reloading the page.
254
+
255
+ If you are exclusively using **fbtee** within Client Components, you can handle locale changes directly on the client only.
256
+
230
257
  ## Usage
231
258
 
232
259
  _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**_
233
260
 
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.
261
+ With **fbtee**, all strings must 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.
235
262
 
236
263
  Here are some basic examples:
237
264
 
@@ -249,7 +276,39 @@ const Greeting = () => (
249
276
  );
250
277
  ```
251
278
 
252
- `<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>`:
279
+ `<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). Here is a more complex example of what you can do with **fbtee**:
280
+
281
+ ```tsx
282
+ <fbt desc="Feed item">
283
+ <fbt:list items={users} name="userList" />{' '}
284
+ <fbt:plural
285
+ count={users.length}
286
+ many="are"
287
+ name="number of players"
288
+ showCount="no"
289
+ >
290
+ is
291
+ </fbt:plural>{' '}
292
+ playing on
293
+ <fbt:param name="mapName">{game.mapName}</fbt:param>
294
+ </fbt>
295
+ ```
296
+
297
+ _Note: `<fbt>` is auto-imported for you by the `@nkzw/babel-preset-fbtee` plugin._
298
+
299
+ This example highlights several features of **fbtee**:
300
+
301
+ - `<fbt:list>`: Renders a list of items, automatically handling conjunctions and delimiters.
302
+ - `<fbt:plural>`: Handles singular and plural forms based on a count value.
303
+ - `<fbt:param>`: Allows you to insert dynamic content into your strings, such as variables or React components.
304
+
305
+ With the above components, you can handle complex strings that are translatable into multiple languages while maintaining clarity and context for translators. This sentence structure supports many variations, such as "Alice is playing on Forest Battle.", or "Alice, Bob and Charlie are playing on Giant Crisis".
306
+
307
+ Let's look at each of these features in detail:
308
+
309
+ ### Dynamic Content
310
+
311
+ **fbtee** supports dynamic content through <fbt:param> or by using other React components within your text. For example, if you want to greet a specific user based on a name passed as a prop, you can use `<fbt:param>`:
253
312
 
254
313
  ```tsx
255
314
  const Greeting = ({ name }) => (
@@ -273,7 +332,74 @@ const Greeting = ({ name }) => (
273
332
  );
274
333
  ```
275
334
 
276
- _Note: `<fbt>` is auto-imported for you by the `@nkzw/babel-preset-fbtee` plugin._
335
+ ### Lists
336
+
337
+ `<fbt:list>` is used to render a list of items with conjunctions and delimiters. It automatically handles the grammatical structure of the list based on the locale:
338
+
339
+ ```tsx
340
+ <fbt:list
341
+ conjunction="or"
342
+ delimiter="comma"
343
+ items={['Alice', 'Bob', <CharlieNameExampleComponent key="charlieUserID" />]}
344
+ name="userList"
345
+ /> // "Alice, Bob, or Charlie"
346
+ ```
347
+
348
+ The default conjunction is "and", and the default delimiter is "comma". You can use "or" or "none" as conjunction, and "bullet", "comma", or "semicolon" as delimiters. The `name` attribute is required to provide context for translators. **fbtee** also exports a `list` function that can be used in contexts outside of React:
349
+
350
+ ```tsx
351
+ import { list } from 'fbtee';
352
+
353
+ const userList = list(['Alice', 'Bob', 'Charlie'], 'or', 'comma');
354
+ ```
355
+
356
+ ### Singular & Plural Forms
357
+
358
+ **fbtee** supports singular and plural forms using the `<fbt:plural>` component. While in English, the plural form is often just an "s" at the end of a word, in other languages, it can be more complex. **fbtee** handles this automatically based on the count value you provide. Here's an example:
359
+
360
+ ```tsx
361
+ <fbt desc="end turn confirmation">
362
+ Do you want to play against
363
+ <fbt:plural count={bots} many="bots" name="numberOfBots" showCount="ifMany">
364
+ a bot
365
+ </fbt:plural>?
366
+ </fbt>
367
+ ```
368
+
369
+ This sentence might be translated to one of the following variations:
370
+
371
+ - "Do you want to play against 1 bot?"
372
+ - "Do you want to play against 2 bots?"
373
+
374
+ `<fbt:plural>` expects the singular phrase as children, and an item `count` as prop. You can provide the plural form using the `many` prop. The `showCount` prop can be used to control whether the count should be displayed in the string. `ifMany` only shows the count in the plural case, while `yes` shows it in all cases, and `no` hides it completely. The `name` prop is required to provide context for translators.
375
+
376
+ ### Using Pronouns
377
+
378
+ You can use `<fbt:pronoun>` to handle pronouns in your strings. This is particularly useful for gendered languages where the pronoun changes based on the subject:
379
+
380
+ ```tsx
381
+ <fbt desc="Photo sharing text">
382
+ <fbt:param name="name">{user.name}</fbt:param>
383
+ shared
384
+ <fbt:pronoun type="possessive" gender={user.pronounGender} human />
385
+ photo with you.
386
+ </fbt>
387
+ ```
388
+
389
+ ### Plain Text Usage
390
+
391
+ In some situations, you may want to use **fbtee** to represent plain text strings without any React components, for example when using alert dialogs or HTML attributes like `placeholder`. For these situations, you can use the `fbs()` function, which is a subset of `fbt` that only supports plain text.
392
+
393
+ ```tsx
394
+ import { fbs } from 'fbtee';
395
+
396
+ <input
397
+ type="text"
398
+ placeholder={fbs('Enter your name', 'Placeholder for name input')}
399
+ />;
400
+ ```
401
+
402
+ ### Extracting Strings & Translating
277
403
 
278
404
  After marking your strings for translation with `<fbt>`, run the following commands to extract, and compile translations:
279
405
 
@@ -281,9 +407,9 @@ After marking your strings for translation with `<fbt>`, run the following comma
281
407
  pnpm fbtee collect
282
408
  ```
283
409
 
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/).
410
+ You can now upload the `source_strings.json` file to your translation provider, for example [Crowdin](https://crowdin.com/). If you'd like to manually edit the strings, you can use the `fbtee prepare-translations --locales de_DE` to generate a JSON file that can be edited directly.
285
411
 
286
- ### Generated File Structure
412
+ #### Generated File Structure
287
413
 
288
414
  Your `source_strings.json` might look like this:
289
415
 
@@ -356,24 +482,23 @@ And German (`translations/de_DE.json`) might look like this:
356
482
  }
357
483
  ```
358
484
 
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:
485
+ Once you have the translated strings stored in a `translations/` folder as JSON files using the locale name, for example `en_US.json` or `de_DE.json`, you can run the following command to generate the translations for use in your app:
360
486
 
361
487
  ```bash
362
488
  pnpm fbtee translate
363
489
  ```
364
490
 
365
- This will generate the `translation.json` file referred to in the setup done before. This file might look like this:
491
+ By default, **fbtee** outputs the generated translations in `src/translations`. It will create one JSON file per locale. For example, if you have `translations/en_US.json` and `translations/de_DE.json`, it will generate `src/translations/en_US.json` and `src/translations/de_DE.json`. These files are an optimized representation of your translation file, ready to be used in your app:
366
492
 
367
493
  ```json
368
494
  {
369
- "de_DE": { "2HVYhv": "Was kommt jetzt?" },
370
- "en_US": { "2HVYhv": "What's next?" }
495
+ "de_DE": {
496
+ "2HVYhv": "Was kommt jetzt?"
497
+ }
371
498
  }
372
499
  ```
373
500
 
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.
375
-
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.
501
+ After generating the translation files, your app is ready to display translated content in other languages. Since the `src/translations` folder is auto-generated, it should be ignored by version control and be part of your build process.
377
502
 
378
503
  ## ESLint Plugin
379
504
 
@@ -401,7 +526,7 @@ If you want stricter enforcement of translation rules, you can use the strict co
401
526
  }
402
527
  ```
403
528
 
404
- Or, if you'd like more granular control over the rules:
529
+ Alternatively, if you'd like more granular control over the rules:
405
530
 
406
531
  ```js
407
532
  {
@@ -423,7 +548,7 @@ The open-source version of `fbt`, however, became unmaintained, difficult to set
423
548
  - **Easier Setup:** fbtee works with modern tools like Vite.
424
549
  - **Statically Typed:** The fbtee compiler ensures correct usage of fbtee, library TypeScript types are provided, and an eslint plugin helps fix common mistakes.
425
550
  - **Improved React Compatibility:** Removed React-specific hacks and added support for implicit React fragments (`<>`).
426
- - **Enhanced Features:** Fixed and exported `inltList` as a new `<fbt:list>` construct, which was not functional in the original `fbt`.
551
+ - **Enhanced Features:** Fixed and exported `intlList` as a new `<fbt:list>` construct, which was not functional in the original `fbt`.
427
552
  - **Modernized Codebase:** Rewritten using TypeScript, ES modules (ESM), eslint, and modern JavaScript standards. Removed cruft and legacy code.
428
553
  - **Updated Tooling:** Uses modern tools like pnpm, Vite, and esbuild for faster and more efficient development of **fbtee**.
429
554
 
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.2.2";
28
+ var version = "1.2.5";
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.2.2";
5
+ var version = "1.2.5";
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.2.2",
3
+ "version": "1.2.5",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "repository": {