Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement i18n package #613

Merged
merged 33 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a59a181
refactor: Abstract WebSocket initialization logic
Jonghakseo Jul 27, 2024
04cc2fb
refactor: Extract HMR code retrieval to a separate module
Jonghakseo Jul 27, 2024
c25ddb5
feat: Add internationalization (i18n) support
Jonghakseo Jul 27, 2024
d87ffb7
feat: add i18n support to NewTab component
Jonghakseo Jul 27, 2024
491fda2
Revert "refactor: Extract HMR code retrieval to a separate module"
Jul 27, 2024
13fe5c4
Revert "refactor: Abstract WebSocket initialization logic"
Jul 27, 2024
8745b94
feat: update i18n usage and message key substitution
Jonghakseo Jul 27, 2024
81a9f83
fix: remove conditional build logic for i18n files
Jul 27, 2024
3420110
feat: Update placeholder format in i18n README
Jonghakseo Jul 27, 2024
9d13edb
fix: correct example output in README.md
Jonghakseo Jul 27, 2024
907f751
refactor: Rename files for consistent naming conventions
Jonghakseo Jul 27, 2024
481a2a8
feat: Rename import alias for i18n module
Jonghakseo Jul 27, 2024
37c64a0
feat: Add auto-generation for locale message handling
Jonghakseo Jul 27, 2024
4fb4f0f
feat: automate i18n generation in build scripts
Jonghakseo Jul 27, 2024
d73fd8c
fix: Correct theme toggle message in Korean locale
Jonghakseo Jul 27, 2024
8546cf0
feat: Add guide for adding and deleting languages in i18n
Jonghakseo Jul 27, 2024
55168d2
chore: remove unused localization file
Jonghakseo Jul 27, 2024
126665c
refactor: Extract build logic to a reusable function
Jonghakseo Jul 27, 2024
72cd6d4
fix: remove .tsx files from build entry points
Jonghakseo Jul 27, 2024
ca88785
fix: correct script and filename for i18n generation
Jonghakseo Jul 27, 2024
49fea64
Merge branch 'main' into feat/i18n
Jonghakseo Jul 27, 2024
f900e43
fix: improve locale detection for default locale
Jonghakseo Jul 27, 2024
ff34280
fix: Correct locale string formatting in generate-i18n script
Jonghakseo Jul 27, 2024
eed6f2b
refactor: reorder message and description fields in i18n
Jonghakseo Jul 27, 2024
d2782ab
Merge branch 'main' into feat/i18n
Jonghakseo Jul 30, 2024
41e1cc2
Merge branch 'main' into feat/i18n
Jonghakseo Aug 4, 2024
8abc4f6
feat: Add placeholder replacement logic in translation function
Jonghakseo Aug 4, 2024
193b0de
Update packages/i18n/lib/i18n-dev.ts
Jonghakseo Aug 6, 2024
81a05f9
Merge branch 'main' into feat/i18n
Jonghakseo Aug 6, 2024
252277e
docs: update example
Aug 6, 2024
e173f7b
Merge branch 'main' into feat/i18n
Jonghakseo Aug 17, 2024
2762ac8
feat: remove unnecessary line break
Jonghakseo Aug 17, 2024
fa0a41b
feat: Add placeholder example
Jonghakseo Aug 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions chrome-extension/public/_locales/en/messages.json

This file was deleted.

2 changes: 2 additions & 0 deletions packages/i18n/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
1 change: 1 addition & 0 deletions packages/i18n/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/i18n.ts
223 changes: 223 additions & 0 deletions packages/i18n/README.md
PatrykKuniczak marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# I18n Package

This package provides a set of tools to help you internationalize your Chrome Extension.

https://developer.chrome.com/docs/extensions/reference/api/i18n

## Installation

If you want to use the i18n translation function in each pages, you need to add the following to the package.json file.

```json
{
"dependencies": {
"@extension/i18n": "workspace:*"
}
}
```

Then run the following command to install the package.

```bash
pnpm install
```

## Manage translations

You can manage translations in the `locales` directory.

`locales/en/messages.json`

```json
{
"helloWorld": {
"message": "Hello, World!"
}
}
```

`locales/ko/messages.json`

```json
{
"helloWorld": {
"message": "안녕하세요, 여러분!"
}
}
```

## Delete or Add a new language

When you want to delete or add a new language, you don't need to edit some util files like `lib/types.ts` or `lib/getMessageFromLocale.ts`.
That's because we provide a script to generate util files automatically by the `generate-i18n.mjs` file.

Following the steps below to delete or add a new language.

### Delete a language

If you want to delete unused languages, you can delete the corresponding directory in the `locales` directory.

```
locales
├── en
│ └── messages.json
└── ko // delete this directory
└── messages.json
```

Then run the following command. (or just run `pnpm dev` or `pnpm build` on root)

```bash
pnpm genenrate-i8n
```

### Add a new language

If you want to add a new language, you can create a new directory in the `locales` directory.

```
locales
├── en
│ └── messages.json
├── ko
│ └── messages.json
└── ja // create this directory
└── messages.json // and create this file
```

Then same as above, run the following command. (or just run `pnpm dev` or `pnpm build` on root)

```bash
pnpm genenrate-i8n
```


## Usage

### Translation function

Just import the `t` function and use it to translate the key.

```typescript
import { t } from '@extension/i18n';

console.log(t('loading')); // Loading...
```

```typescript jsx
import { t } from '@extension/i18n';

const Component = () => {
return (
<button>
{t('toggleTheme')} // Toggle Theme
</button>
);
};
```

### Placeholders

If you want to use placeholders, you can use the following format.

> For more information, see the [Message Placeholders](https://developer.chrome.com/docs/extensions/how-to/ui/localization-message-formats#placeholders) section.

`locales/en/messages.json`

```json
{
"greeting": {
Jonghakseo marked this conversation as resolved.
Show resolved Hide resolved
"description": "Greeting message",
"message": "Hello, My name is $NAME$",
"placeholders": {
"name": {
"content": "$1",
"example": "John Doe"
}
}
},
"hello": {
"description": "Placeholder example",
"message": "Hello $1"
}
}
```

`locales/ko/messages.json`

```json
{
"greeting": {
"description": "인사 메시지",
"message": "안녕하세요, 제 이름은 $NAME$입니다.",
"placeholders": {
"name": {
"content": "$1",
"example": "서종학"
}
}
},
"hello": {
"description": "Placeholder 예시",
"message": "안녕 $1"
}
}
```

If you want to replace the placeholder, you can pass the value as the second argument.

Function `t` has exactly the same interface as the `chrome.i18n.getMessage` function.

```typescript
import { t } from '@extension/i18n';

console.log(t('greeting', 'John Doe')); // Hello, My name is John Doe
console.log(t('greeting', ['John Doe'])); // Hello, My name is John Doe

console.log(t('hello')); // Hello
console.log(t('hello', 'World')); // Hello World
console.log(t('hello', ['World'])); // Hello World
```

### Locale setting on development

If you want to show specific language, you can set the `devLocale` property. (only for development)

```typescript
import { t } from '@extension/i18n';

t.devLocale = "ko";

console.log(t('hello')); // 안녕
```

### Type Safety

When you forget to add a key to all language's `messages.json` files, you will get a Typescript error.

`locales/en/messages.json`

```json
{
"hello": {
"message": "Hello World!"
}
}
```

`locales/ko/messages.json`

```json
{
"helloWorld": {
"message": "안녕하세요, 여러분!"
}
}
```

```typescript
import { t } from '@extension/i18n';

// Error: TS2345: Argument of type "hello" is not assignable to parameter of type
console.log(t('hello'));
```
6 changes: 6 additions & 0 deletions packages/i18n/build.dev.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import path from "node:path";
import { build } from "./build.mjs";

const i18nPath = path.resolve("lib", "i18n-dev.ts");

void build(i18nPath);
31 changes: 31 additions & 0 deletions packages/i18n/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import esbuild from "esbuild";
import fs from "node:fs";
import path from "node:path";
import { rimraf } from "rimraf";

/**
* @param i18nPath {string}
*/
export async function build(i18nPath) {
fs.cpSync(i18nPath, path.resolve("lib", "i18n.ts"));
PatrykKuniczak marked this conversation as resolved.
Show resolved Hide resolved

await esbuild.build({
entryPoints: ["./index.ts"],
tsconfig: "./tsconfig.json",
bundle: true,
packages: "bundle",
target: "es6",
outdir: "./dist",
sourcemap: true,
format: "esm"
});

const outDir = path.resolve("..", "..", "dist");
const localePath = path.resolve(outDir, "_locales");
rimraf.sync(localePath);
fs.cpSync(path.resolve("locales"), localePath, { recursive: true });
PatrykKuniczak marked this conversation as resolved.
Show resolved Hide resolved

console.log("I18n build complete");
}


6 changes: 6 additions & 0 deletions packages/i18n/build.prod.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import path from "node:path";
import { build } from "./build.mjs";

const i18nPath = path.resolve("lib", "i18n-prod.ts");

void build(i18nPath);
Loading