Skip to content

Commit

Permalink
feat: New @wxt-dev/i18n package (#758)
Browse files Browse the repository at this point in the history
  • Loading branch information
aklinker1 authored Aug 19, 2024
1 parent 751706d commit 5821ae0
Show file tree
Hide file tree
Showing 30 changed files with 2,002 additions and 81 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ on:
default: wxt
type: choice
options:
- wxt
- auto-icons
- i18n
- module-react
- module-vue
- module-svelte
- module-solid
- auto-icons
- module-svelte
- module-vue
- wxt

jobs:
validate:
Expand Down
8 changes: 8 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export default defineConfig({
menuGroup('Extension APIs', '/guide/extension-apis/', [
menuItem('Storage', 'storage'),
menuItem('Messaging', 'messaging'),
menuItem('I18n', 'i18n'),
menuItem('Scripting', 'scripting'),
menuItem('Others', 'others'),
]),
Expand All @@ -148,6 +149,13 @@ export default defineConfig({
menuGroup('Upgrade Guide', '/guide/upgrade-guide/', [
menuItem('wxt', 'wxt'),
]),
menuGroup('@wxt-dev/i18n', '/guide/i18n/', [
menuItem('Introduction', 'introduction.md'),
menuItem('Installation', 'installation.md'),
menuItem('Messages File Format', 'messages-file-format.md'),
menuItem('Build Integrations', 'build-integrations.md'),
menuItem('Editor Support', 'editor-support.md'),
]),
]),
'/api/': menuRoot([
menuGroup('CLI', '/api/cli/', [
Expand Down
56 changes: 56 additions & 0 deletions docs/guide/extension-apis/i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Internationalization

This guide is for using the vanilla, `browser.i18n` APIs. There are two other alternatives:

1. [`@wxt-dev/i18n`](/guide/i18n/installation) - a wrapper around `browser.i18n` APIs with additional features, a simplified localization file format, and editor support
2. Third party packages - You can use any i18n package on NPM, most of which are more feature rich than `browser.i18n` and `@wxt-dev/i18n`

:::info
Currently, using the `browser.i18n` APIs are the recommended approach. WXT has some built-in support for them and they work well enough. `@wxt-dev/i18n` was recently released, and it will become the recommended approach after some of the bugs have been worked out. Head over to [it's docs](/guide/i18n/introduction.md) to learn more.
:::

## Setup

First familiarize yourself with [Chrome's docs](https://developer.chrome.com/docs/extensions/reference/api/i18n). The only difference when using these APIs with WXT is where you put the localization files - in the [`public` directory](/guide/directory-structure/public/).

```
<srcDir>/
└─ public/
└─ _locales/
├─ en/
│ └─ messages.json
├─ de/
│ └─ messages.json
└─ ko/
└─ messages.json
```

Next, to set a `default_locale` on your manifest, add it to your `wxt.config.ts` file:

```ts
// wxt.config.ts
export default defineConfig({
manifest: {
default_locale: 'en',
name: '__MSG_extName__',
description: '__MSG_extDescription__',
},
});
```

> You can localize the `name` and `description` of your extension from the `manifest` config as well.
Finally, to get a translation, call `browser.i18n.getMessage`:

```ts
browser.i18n.getMessage('extName');
browser.i18n.getMessage('extDescription');
browser.i18n.getMessage(/* etc */);
```

## Examples

See the official localization examples for more details:

- [I18n](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-i18n#readme)
- [Vue I18n](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vue-i18n#readme)
82 changes: 82 additions & 0 deletions docs/guide/i18n/build-integrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Build Integrations

To use the custom messages file format, you'll need to use `@wxt-dev/i18n/build` to transform the custom format to the one expected by browsers.

Here's a list of build tools that already have an integration:

[[toc]]

:::info
If you want to contribute, please do! In particular, an `unplugin` integration would be awesome!
:::

## WXT

See [Installation with WXT](./installation#with-wxt).

But TLDR, all you need is:

```ts
// wxt.config.ts
export default defineConfig({
modules: ['@wxt-dev/i18n/module'],
});
```

Types are generated whenever you run `wxt prepare` or another build command:

```sh
wxt prepare
wxt
wxt build
wxt zip
# etc
```

## Custom

If you're not using WXT, you'll have to pre-process the localization files yourself. Here's a basic script to generate the `_locales/.../messages.json` and `wxt-i18n-structure.d.ts` files:

```ts
// build-i18n.js
import {
parseMessagesFile,
generateChromeMessagesFile,
generateTypeFile,
} from '@wxt-dev/i18n/build';

// Read your localization files
const messages = {
en: await parseMessagesFile('path/locales/en.yml'),
de: await parseMessagesFile('path/locales/de.yml'),
// ...
};

// Generate JSON files for the browser
await generateChromeMessagesFile('dist/_locales/en/messages.json', messages.en);
await generateChromeMessagesFile('dist/_locales/de/messages.json', messages.de);
// ...

// Generate a types file based on your default_locale
await generateTypeFile('wxt-i18n-structure.d.ts', messages.en);
```

Then run the script:

```sh
node generate-i18n.js
```

If your build tool has a dev mode, you'll also want to rerun the script whenever you change a localization file.

### Type Safety

Once you've generated `wxt-i18n-structure.d.ts` (see the [Custom](#custom) section), you can use it to pass the generated type into `createI18n`:

```ts
import type { WxtI18nStructure } from './wxt-i18n-structure';

export const i18n = createI18n<WxtI18nStructure>();
```

And just like that, you have type safety!
48 changes: 48 additions & 0 deletions docs/guide/i18n/editor-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Editor Support

For better DX, you can configure your editor with plugins and extensions.

[[toc]]

## VS Code

Install the [I18n Ally Extension](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) to:

- Go to translation definition
- Inline previews of text
- Hover to see other translations

You'll need to configure it the extension so it knows where your localization files are and what function represents getting a translation:

`.vscode/i18n-ally-custom-framework.yml`:

```yml
# An array of strings which contain Language Ids defined by VS Code
# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers
languageIds:
- typescript

# Look for t("...")
usageMatchRegex:
- "[^\\w\\d]t\\(['\"`]({key})['\"`]"

# Disable other built-in i18n ally frameworks
monopoly: true
```
`.vscode/settings.json`:

```json
{
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested"
}
```

## Zed

As of time of writing, Aug 18, 2024, no extensions exist for Zed to add I18n support.

## IntelliJ

Unknown - Someone who uses IntelliJ will have to open a PR for this!
81 changes: 81 additions & 0 deletions docs/guide/i18n/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Installation

[[toc]]

### With WXT

1. Install `@wxt-dev/i18n` via your package manager:

```sh
pnpm i @wxt-dev/i18n
```

2. Add the WXT module to your `wxt.config.ts` file and setup a default locale:

```ts
export default defineConfig({
modules: ['@wxt-dev/i18n/module'],
manifest: {
default_locale: 'en',
},
});
```

3. Create a localization file at `<srcDir>/locales/<default_locale>.yml` or move your existing localization files there.

```yml
# <srcDir>/locales/en.yml
helloWorld: Hello world!
```

:::tip
`@wxt-dev/i18n` supports the standard messages format, so if you already have localization files at `<srcDir>/public/_locale/<lang>/messages.json`, you don't need to convert them to YAML or refactor them - just move them to `<srcDir>/locales/<lang>.json` and they'll just work out of the box!
:::

4. To get a translation, use the auto-imported `i18n` object or import it manually:

```ts
import { i18n } from '#i18n';
i18n.t('helloWorld'); // "Hello world!"
```
And you're done! Using WXT, you get type-safety out of the box.
### Without WXT
1. Install `@wxt-dev/i18n` via your package manager:
```sh
pnpm i @wxt-dev/i18n
```
2. Create a messages file at `_locales/<lang>/messages.json` or move your existing translations there:
```json
{
"helloWorld": {
"message": "Hello world!"
}
}
```
:::info
For the best DX, you should to integrate `@wxt-dev/i18n` into your build process. This enables:
1. Plural forms
2. Simple messages file format
3. Type safety
See [Build Integrations](./build-integrations) to set it up.
:::
3. Create the `i18n` object, export it, and use it wherever you want!
```ts
import { createI18n } from '@wxt-dev/i18n';
export const i18n = createI18n();
i18n.t('helloWorld'); // "Hello world!";
```
22 changes: 22 additions & 0 deletions docs/guide/i18n/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Introduction

:::info
You don't have to use `wxt` to use this package - it will work in any bundler setup. See [Installation without WXT](./installation#without-wxt) for more details.
:::

`@wxt-dev/i18n` is a simple, type-safe wrapper around the `browser.i18n` APIs. It provides several benefits over the standard API:

1. Simple messages file format
2. Plural form support
3. Integrates with the [I18n Ally VS Code extension](./editor-support#vscode)

It also provides several benefits over other non-web extension specific i18n packages:

1. Does not bundle localization files into every entrypoint
2. Don't need to fetch the localization files asynchronously
3. Can localize extension name in manifest
4. Can access localized strings inside CSS files

However, it does have one major downside:

1. Like the `browser.i18n` API, to change the language, users must change the browser's language
Loading

0 comments on commit 5821ae0

Please sign in to comment.