Skip to content

RESTAR-inc/re-i18n

Repository files navigation

Re-i18n

A tool to manage i18n by extracting keys from source code and generating locale files.

Usage

Locale Locator

Create a module that exports a function that returns the current locale. This function will be used to determine the locale of generated files. The path to this module must be specified in the generate.localeLocatorPath parameter of the configuration file. Depending on the configuration of your project (appType parameter), the implementation of the function will be different (for example, a Vue project may need to use reactivity, while a vanilla project may need to return a simple string).

import type { UseLocaleLocator } from "@restar-inc/re-i18n/lib/vendor/vue";
import { computed, ref } from "vue";

type Locale = "ja" | "en";

const innerLocale = ref<Locale>("ja");

const useLocaleLocator: UseLocaleLocator<Locale> = (locales, defaultLocale) => {
  return computed({
    get: () => innerLocale.value,
    set: (value) => {
      if (locales.includes(value)) {
        innerLocale.value = value;
      } else {
        innerLocale.value = defaultLocale;
      }
    },
  });
};

export default useLocaleLocator;

Formatter (optional)

If you need to format translations, create a module that exports a function that takes a key and a value as parameters and returns a formatted string. The path to this module must be specified in the generate.formatterPath parameter of the configuration file.

import formatString from "my-amazing-formatter-lib"; // just an example
import type { I18nFormatter } from "@restar-inc/re-i18n";

type Locale = "ja" | "en";

const fmt: I18nFormatter<Locale> = (locale, str, opts) => {
  return formatString(str, opts, locale);
};
export default fmt;

Configuration

Create a re-i18n.config.json file in the root of your project. Follow the example below to configure it. Refer to the ./src/schemas/config.ts file for details.

{
  "defaultLocale": "ja",
  "locales": ["ja", "en"],
  "appType": "vue",
  "pattern": "src/{app,lib}/**/*.{ts,vue}",
  "json": {
    "outDir": "./out/json"
  },
  "csv": {
    "outDir": "./out/csv",
    "inputDir": "./out/csv",
    "delimiter": ";"
  },
  "xls": {
    "outDir": "./out/xls",
    "inputDir": "./out/xls"
  },
  "generate": {
    "localeLocatorPath": "./src/i18n/useLocateLocator",
    "formatterPath": "./src/i18n/formatter"
  }
}

Translation Keys

After following the previous steps, you can use the t function to define translation keys in your source code. You can use a different name for the function by setting the funcName parameter in the configuration file.

Let's suppose you have the following code:

// src/lib/myAwesomeModule.ts

function sayHello(name: string) {
  return `こんにちわ${name}さん`;
}

// src/lib/MyAwesomeComponent.vue
<script setup lang="ts">
const props = defineProps({
  name: String,
});

const showAlert = () => alert(`おはようございます${props.name}さん`);
</script>

<template>
  <button @click="showAlert">こんばんは</button>
</template>

Set the keys as follows:

// src/MyAwesomeModule/hello.ts

function sayHello(name: string) {
  return t("こんにちわ{name}さん", { name });
}

// src/MyAwesomeModule/Component.vue
<script setup lang="ts">
const props = defineProps({
  name: String,
});

const showAlert = () => alert(
  t("おはようございます{name}さん", { name: props.name })
);
</script>

<template>
  <button @click=showAlert>{{ t("こんばんは") }}</button>
</template>

Next step is to run comman re-i18n generate. This will generate locale files in the out directory.

src/MyAwesomeModule
├── Component.vue
├── hello.ts
└── locales
    ├── en.json
    ├── index.ts
    └── ja.json
// src/MyAwesomeModule/locales/en.json
{
  "おはようございます{name}さん": "",
  "こんにちわ{name}さん": "",
  "こんばんは": ""
}

// src/MyAwesomeModule/locales/ja.json
{
  "おはようございます{name}さん": "",
  "こんにちわ{name}さん": "",
  "こんばんは": ""
}

Next step is to import t function from locale folder:

// src/MyAwesomeModule/hello.ts
import { t } from "./locales";
...

// src/MyAwesomeModule/Component.ts
<script setup lang="ts">
import { t } from "./locales";
...
</script>

Now you can put the translations in the locale files.

For more details, see the example directory.

Vendor Specific Features

Vue Reactivity

By default, the translation function returns a string. If you want to use reactivity, you can use the composable function useReI18n or its shortcut t.$.

// re-i18n.config.json
{
  "funcName": "t",
  "composableName": "useMyReI18n" // default `useReI18n`
}
// Component.vue
<script setup lang="ts">
import { t, useMyReI18n } from "./locales";

const MSG_1 = t("メッセージ 1"); // MSG_1 is `string`
const MSG_2 = t.$("メッセージ 2"); // MSG_2 is `ComputedRef<string>`
const MSG_3 = useMyReI18n("メッセージ 3"); // MSG_3 is `ComputedRef<string>`
</script>

NOTE: t.$ is just a shortcut for useReI18n, it works exactly the same way. A typical use in the <script setup> section is to use t.$ in nested object fields:

// Instead of this
import { t, useMyReI18n } from "./locales";
const msg1 = useMyReI18n("メッセージ 1");
const msg2 = useMyReI18n("メッセージ 2");

const MESSAGES = {
  msg1,
  msg2,
};

// You can do this
const MESSAGES = {
  msg1: t.$("メッセージ 1"),
  msg2: t.$("メッセージ 2"),
};

IMPORTANT: If you use t.$ inside object fields, such as const obj = { msg: t.$("message") }, reactivity will work, but you will have to manually unwrap the value with obj.msg.value. This is not related to this package, but is a limitation of Vue.

<script setup lang="ts">
import { t } from "./locales";

const MSG_1 = t.$("メッセージ 1");
const MSG_2 = {
  msg: t.$("メッセージ 2");
}
</script>
<template>
  <p>{{ MSG_1 }}</p><!-- メッセージ 1 -->
  <p>{{ MSG_2.msg }}</p><!-- "メッセージ 2" -->
  <p>{{ MSG_2.msg.value }}</p><!-- メッセージ 2 -->
</template>

Vue Component

You can also use vendor specific features. For example, if you are using Vue, you can use the ReI18n component to display translation keys in source code and use VDOM nodes as parameters.

IMPORTANT: If you use t inside child nodes, reactivity will not work

First you need to specify the component name in the configuration file.

{
  "componentName": "MyI18n" // default `ReI18n`
}

Then you can use it in your source code.

<script setup lang="ts">
import { MyI18n } from "./locales";
</script>

<template>
  <MyI18n msg="赤い:{0} 緑:{1} 青い:{2}">
    <div class="bg-red-400 w-10 h-10 rounded-full"></div>
    <div class="bg-green-400 w-10 h-10 rounded-full"></div>
    <div class="bg-blue-400 w-10 h-10 rounded-full"></div>
  </MyI18n>
</template>

Commands

Depending on your project configuration (funcName param),

This package provides the CLI command re-i18n. Run it in the root of your project.

re-i18n --help

generate

Generates locale files from source code. Uses the pattern and appType parameters defined in the configuration file to parse the source code, read the i18n keys, generate the i18n files, and write them to the dirName directory. It also uses the generator.localeLocatorPath and generator.formatterPath functions defined in the config file to generate the main module in each locale directory.

export-json

Creates a JSON file from the source code and the i18n keys found. This is essentially an AST representation of the code used to generate i18n files. It is not intended for direct use.

export-csv

For each language, a CSV file is created from the source code and the i18n keys found.

export-xls

Creates an XLS file from the source code and the i18n keys found.

import-csv

For each locale, it will read the CSV file and overwrite the i18n files with the new values.

import-xls

Reads the XLS file and overwrites the i18n files with the new values.

stats

Displays statistics on the number of keys in each locale.

lint

Checks for missing and obsolete keys in the source code and i18n files.