@vasakgroup/tauri-plugin-i18n
v2.3.0
Published
Tauri internationalization using rust_i18n
Readme
Tauri Plugin i18n (A simple wrapper around rust_i18n)
this project is a one fork of tauri-plugin-i18n
This plugin does not support macros from rust_i18n.
rust_i18n is only used for parsing the locales.

| Platform | Supported | | -------- | --------- | | Linux | ✓ | | Windows | ✓ | | macOS | ✓ | | Android | Untested | | iOS | Untested |
Install
This plugin requires a Rust version of at least 1.77.2
Install the Core plugin by adding the following to your Cargo.toml file:
src-tauri/Cargo.toml
# Point this to your fork's repository and branch/tag/rev
# Example using a GitHub repo:
[dependencies.tauri-plugin-i18n-vsk]
git = "https://github.com/VasakOS/tauri-plugin-i18n"
# Or use a local path if developing locally:
# path = "../path/to/your/fork/tauri-plugin-i18n"The package can also be installed by using cargo:
cargo add tauri-plugin-i18n-vskYou can install the JavaScript Guest bindings using your preferred JavaScript package manager:
Install the JavaScript bindings using your preferred package manager:
# Using bun
bun add @vasakgroup/tauri-plugin-i18n
# Using pnpm
pnpm add @vasakgroup/tauri-plugin-i18n
# Using npm
npm install @vasakgroup/tauri-plugin-i18n
# Using yarn
yarn add @vasakgroup/tauri-plugin-i18nLocale file [rust_i18n implementation]
You can use _version key to specify the version (This version is the locale file version, not the rust-i18n version) of the locale file, and the default value is 1.
rust-i18n supports two style of config file, and those versions will always be keeping.
_version: 1- Split each locale into difference files, it is useful when your project wants to split to translate work._version: 2- Put all localized text into same file, it is easy to translate quickly by AI (e.g.: GitHub Copilot). When you write original text, just press Enter key, then AI will suggest you the translation text for other languages.
You can choose as you like.
Split Localized Texts into Difference Files
_version: 1
You can also split the each language into difference files, and you can choose (YAML, JSON, TOML), for example: en.json:
.
├── Cargo.lock
├── Cargo.toml
├── locales
│ ├── zh-CN.yml
│ ├── en.yml
└── src
│ └── main.rs_version: 1
hello: 'Hello world'
mello: 'Mello world'Or use JSON or TOML format, just rename the file to en.json or en.toml, and the content is like this:
{
"_version": 1,
"hello": "Hello world",
"mello": "Mello world"
}hello = "Hello world"
mello = "Mello world"All Localized Texts in One File
_version: 2
Make sure all localized files (containing the localized mappings) are located in the locales/ folder of the project root directory:
.
├── Cargo.lock
├── Cargo.toml
├── locales
│ ├── app.yml
│ ├── some-module.yml
└── src
│ └── main.rs
└── sub_app
│ └── locales
│ │ └── app.yml
│ └── src
│ │ └── main.rs
│ └── Cargo.tomlIn the localized files, specify the localization keys and their corresponding values, for example, in app.yml:
_version: 2
hello:
en: Hello world
zh-CN: 你好世界This is useful when you use GitHub Copilot, after you write a first translated text, then Copilot will auto generate other locale's translations for you.
Usage
First you need to register the core plugin with Tauri:
The init method takes two args:
- The location of the locales dir
- Default locale (eg: "en")
src-tauri/src/lib.rs
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_i18n::init(None))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Afterwards all the plugin's APIs are available through the JavaScript guest bindings and also via tauri::AppHandle:
JS
import I18n from '@vasakgroup/tauri-plugin-i18n';
// Load translations
await I18n.getInstance().load();
// Example
//Get available locales
const locales = await I18n.getAvailableLocales();
console.log('Locales:', locales);Rust
#[tauri::command]
fn get_locales(app: tauri::AppHandle)-> Vec<String> {
app.i18n().available_locales()
}Updating the locale
JS
import I18n from '@vasakgroup/tauri-plugin-i18n';
// Example
// Set the locale
await I18n.setLocale('en');Rust
#[tauri::command]
fn update_locale(app: tauri::AppHandle, locale: &str) {
app.i18n().set_locale(locale);
}Using the locale
Note: The i18n class must be instantiated in order to translate in javascript.
JS
// In javascript, it uses the [data-i18n] attribute to remain framework agnostic
import I18n from '@vasakgroup/tauri-plugin-i18n';
// Load translations
await I18n.getInstance().load();
// Example
// Use a translation
<p data-i18n="hello"> </p>;Rust
let translated: Option<&str> = app.i18n().translate("hello");Call the destroy method in javascript when done to cleanup.
See the examples folder for a working app.
Vue: useI18n()
The composable reads the same state as the I18n class — it does not keep a
copy. Loading through either one fills both, so the recommended startup is to
load once before mounting and let every component just read:
// main.ts
import I18n from '@vasakgroup/tauri-plugin-i18n';
// One load for the whole app. Every `useI18n()` below sees it already done.
await I18n.getInstance().load();
app.mount('#app');<script setup>
import { useI18n } from '@vasakgroup/tauri-plugin-i18n'
const { t, locale, setLocale, availableLocales, isLoaded, reload } = useI18n()
</script>
<template>
<p>{{ t('views.home.title') }}</p>
</template>load() is safe to call from anywhere and as often as you like: the first call
does the work and the rest await that same one. If a component mounts before
anything loaded, its onMounted loads for everyone. Use reload() only to
re-read the catalogs after the files on disk changed.
Before 2.3.0 the composable kept its own state, so
await I18n.getInstance().load()before mounting did not give the components anything: the window showed raw keys (views.home.title) until each component loaded again on its own. Upgrading fixes that without any change in the app.
Watching the state from outside Vue
const i18n = I18n.getInstance();
const unsubscribe = i18n.subscribe(() => {
console.log(i18n.currentLocale, i18n.catalogs);
});subscribe() fires when the catalogs or the locale change and returns the
function to unsubscribe. catalogs and currentLocale read the state at any
moment, which is what lets a late subscriber catch up.
Companion package
Use package rust-i18n-autotranslate to autotranslate locales from a source locale.
Use .taurignore to prevent looping while running build.
This is because tauri tracks all files in the project unless explicitly ignored.
It can be used both at compile time and runtime
Compile time
//build.rs
use rust_i18n_autotranslate::{
TranslationAPI,
config::{Config, TranslationProvider},
};
fn main() {
//run translations
let target_locales = [
"es", "zh-CN", "zh-TW", "hi", "ar", "fr", "pt-BR", "de", "ru", "ja", "ko", "it", "tr",
"id", "vi",
];
let cfg = Config::new()
.locales_directory("./locales")
.source_lang("en")
.add_target_langs(target_locales.to_vec())
.use_cache(true)
.translation_provider(TranslationProvider::DEEPL)
.build();
TranslationAPI::translate(cfg).unwrap();
tauri_build::build();
}
