@nkzw/eslint-plugin-fbtee 1.1.0 → 1.2.1
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 +159 -34
- package/lib/index.cjs +1 -1
- package/lib/index.js +1 -1
- package/package.json +1 -1
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
|
|
8
|
-
- **Proven in Production:** Built on Facebook's `fbt`, with over a decade of production usage, serving billions of users and
|
|
9
|
-
- **Optimized Performance with IR:** Compiles translations into an Intermediate Representation (IR) for extracting strings,
|
|
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
|
-
|
|
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:
|
|
53
|
-
"fbtee:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
110
|
+
src/translations/
|
|
78
111
|
```
|
|
79
112
|
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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>`
|
|
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`,
|
|
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.1
|
|
28
|
+
var version = "1.2.1";
|
|
29
29
|
var description = "";
|
|
30
30
|
var keywords = [];
|
|
31
31
|
var repository = {
|
package/lib/index.js
CHANGED