# i18n
Simple i18n implementation with API inspired by vue-i18n (opens new window)
# Parameters
import { useI18n, buildI18n, setI18n } from "vue-composable";
interface i18nOption {
  locale: string; // start locale
  fallback?: string; // fallback locale
  // override the default resolver
  resolve?: (
    i18n: i18n, // current locale messages
    path: Readonly<RefTyped<string>>,
    args: RefTyped<FormatObject> | Array<FormatValue> |  undefined
  ) => RefTyped<string>;
  notFoundFallback?: boolean, // fallback message if the key not found in the current locale `default:true`
    /*
   localized messages, the first level property is the locale name
  */
  messages: {
    en: {
      /*object based messages*/
    },
    'en-GB': {
      /*object based messages*/
    },
    pt: ()=> import('/locale/pt').default, // also supports lazy loading
  },
}
useI18n(options);
// dependency injection i18n
setI18n(options);
// inject i18n if no arguments passed
useI18n(); // retrieves the i18n
// retrieves i18n object without injecting
buildI18n(options);
| Parameters | Type | Required | Description | 
|---|---|---|---|
| option | i18nOption |  false | i18n options | 
# Typescript
You can specify the i18n types in multiple ways
# Options
If options are passed the i18n result will be based on the definition locale and the messages[locale].
const { i18n } = useI18n({
  locale: "en",
  fallback: "en",
  messages: {
    en: {
      hello: "hello world",
    },
    pt: {
      hello2: "Olá mundo",
    },
  },
});
i18n.hello; // correct, because is the selected locale
i18n.hello2; // typescript error, it doesn't exist in the type
# Templated
If the i18n definition is injected in the parent component, you can pass the messages using templated overload
// types.d.ts
interface I18nMessages {
  hello: string;
}
// component.vue
import { I18nMessages } from "./types.d.ts";
const { i18n } = useI18n<I18nMessages>();
# Global definition
If you don't want to import the type on every component, you can describe it globally
// types.d.ts
import { i18n } from "vue-composable";
declare module "vue-composable" {
  interface i18n {
    hello: string;
  }
}
// component.vue
const { i18n } = useI18n();
i18n.hello; // typescript has the definition
# JSON file
You may have the translations in a json file, you can use the types from that json file.
// tsconfig.json
{
  "compilerOptions": {
    "resolveJsonModule": true,
  }
}
// locales/en.json
{
  "hello": "Hello"
}
// types.d.ts
import { i18n } from "vue-composable";
type json = typeof import("@/locales/en.json");
declare module "vue-composable" {
  interface i18n extends json {}
}
// App.vue
import {setI18n} from 'vue-composable'
import en from  '@/locales/en.json',
setup(){
  setI18n({
    locale: 'en',
    messages: {
      en,
      es: ()=> import('@/locales/es.json'),
      // etc...
    }
  })
}
// Child.vue
const { i18n } = useI18n();
i18n.hello; // typescript has the definition
i18n.hello2; // type error
# State
The useI18n function exposes the following reactive state:
import { useI18n } from "vue-composable";
const { locale, locales, i18n } = useI18n();
| State | Type | Description | 
|---|---|---|
| locale | Ref<String> |  Current locale | 
| locales | Ref<String> |  Array of available locales | 
| i18n | Ref<i18n> |  Object based i18n access | 
# Methods
The useI18n function exposes the following methods:
import { useI18n } from "vue-composable";
const { $t, $ts, addLocale, removeLocale } = useI18n();
| Signature | Description | 
|---|---|
$t(path, args?) |  Retrieve localised message | 
$ts(path, args?) |  Same as $t but returns string instead of ref<string> | 
addLocale(locale, messages) |  add new locale | 
removeLocale(locale) |  remove locale | 
# Example
Hello
Today is: Sunday
Today is: Sunday
Custom locale
# Code
<template>
  <div>
    <div>
      <select v-model="locale">
        <option v-for="l in locales" :key="l" :value="l">
          {{ i18n.locales[l] }}
        </option>
      </select>
      <div>
        <label for="name">{{ i18n.input.name.label }}</label>
        <input
          name="name"
          v-model="name"
          :placeholder="i18n.input.name.placeholder"
        />
      </div>
    </div>
    <h1>{{ i18n.title }}</h1>
    <h5>{{ $t("hello", { name }).value }}</h5>
    <p>
      {{
        $t("currentDate", { day: $t(`weekDays[${new Date().getDay()}]`) }).value
      }}
    </p>
    <p>
      {{ $t("currentDate", { day: i18n.weekDays[new Date().getDay()] }).value }}
    </p>
    <hr />
    <h3>Custom locale</h3>
    <div>
      <button v-if="locales.indexOf('custom') < 0" @click="addCustomLocale">
        Add custom locale
      </button>
      <button v-else @click="removeCustomLocale">Remove custom locale</button>
    </div>
    <textarea v-model="customLocaleJson"></textarea>
  </div>
</template>
<script>
import Vue from "vue";
import { useI18n, promisedTimeout } from "vue-composable";
import { ref, computed, watch, i18n } from "@vue/composition-api";
export default {
  setup() {
    const name = ref("");
    const i18n = useI18n({
      locale: "en",
      fallback: "en",
      messages: {
        en: {
          locales: {
            en: "English",
            pt: "Portuguese",
            es: "Spanish",
            custom: "YourLocale"
          },
          weekDays: [
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday"
          ],
          input: {
            name: {
              label: "Name",
              placeholder: "Your name"
            }
          },
          hello: "Hello {name}",
          currentDate: "Today is: {day}"
        },
        pt: {
          locales: {
            en: "Inglês",
            pt: "Português",
            es: "Espanhol"
          },
          weekDays: [
            "Domingo",
            "Segunda-feira",
            "Terca-feira",
            "Quarta-feira",
            "Quinta-feira",
            "Sexta-feira",
            "Sábado"
          ],
          input: {
            name: {
              label: "Nome",
              placeholder: "O teu nome"
            }
          },
          hello: "Olá {name}"
          currentDate: "Hoje e': {day}",
        },
        // promised based
        es: () =>
          promisedTimeout(10).then(() => ({
            locales: {
              en: "Ingles",
              pt: "Portugués",
              es: "Espanol"
            },
            weekDays: [
              "Domingo",
              "Lunes",
              "Martes",
              "Miércoles",
              "Jueves",
              "Viernes",
              "Sábado"
            ],
            input: {
              name: {
                label: "Nombre",
                placeholder: "Tu nombre"
              }
            },
            hello: "Holla {name}"
            currentDate: "Hoy es: {day}",
          }))
      }
    });
    const customLocale = ref({
      locales: {
        custom: "Awesome"
      },
      hello: "H3Y"
    });
    const customLocaleJson = ref(JSON.stringify(customLocale.value));
    const addCustomLocale = () => {
      i18n.addLocale("custom", customLocale.value);
      i18n.locale.value = "custom";
    };
    const removeCustomLocale = () => i18n.removeLocale("custom");
    watch(
      customLocaleJson,
      json => {
        try {
          customLocale.value = JSON.parse(json);
        } catch (e) {
          // err
        }
      },
      { lazy: true }
    );
    return {
      ...i18n,
      name,
      customLocaleJson,
      addCustomLocale,
      removeCustomLocale
    };
  }
};
</script>
← Validation Title →