feat: adding initial i18n support

This commit is contained in:
Matt Kilgore
2024-08-03 23:04:26 -04:00
parent 0bcb155756
commit 3540ce4297
7 changed files with 7451 additions and 5316 deletions

View File

@@ -1,6 +1,6 @@
<template>
<div class="py-4">
<p class="text-sm">Password Strength: {{ message }}</p>
<p class="text-sm">{{ $t("password_strength") }}: {{ message }}</p>
<progress
class="progress w-full progress-bar"
:value="score"

17
frontend/locales/en.json5 Normal file
View File

@@ -0,0 +1,17 @@
{
"tagline": "Track, Organize, and Manage your Things.",
"version": "Version",
"build": "Build",
"disabled_registration": "Registration Disabled",
"login": "Login",
"register": "Register",
"remember_me": "Remember Me",
"password": "Password",
"email": "Email",
"set_email": "What's your email?",
"set_password": "Set your password",
"set_name": "What's your name?",
"password_strength": "Password Strength",
"joining_group": "You're Joining an Existing Group!",
"dont_join_group": "Don't want to join a group?",
}

View File

@@ -1,8 +1,21 @@
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "url";
import { defineNuxtConfig } from "nuxt/config";
import VueI18nVitePlugin from "@intlify/unplugin-vue-i18n/vite";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
ssr: false,
build: {
transpile: ["vue-i18n"],
},
vite: {
plugins: [
VueI18nVitePlugin({
include: [resolve(dirname(fileURLToPath(import.meta.url)), "./locales/**")],
}),
],
},
modules: [
"@nuxtjs/tailwindcss",
"@pinia/nuxt",

View File

@@ -16,6 +16,7 @@
"devDependencies": {
"@faker-js/faker": "^8.0.0",
"@iconify-json/mdi": "^1.1.64",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@types/dompurify": "^3.0.0",
"@types/markdown-it": "^13.0.0",
@@ -27,13 +28,15 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.4.0",
"h3": "^1.7.1",
"intl-messageformat": "^10.5.14",
"isomorphic-fetch": "^3.0.0",
"nuxt": "3.6.5",
"prettier": "^3.2.5",
"typescript": "^5.0.0",
"unplugin-icons": "^0.18.5",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^1.0.0"
"vitest": "^1.0.0",
"vue-i18n": "^9.13.1"
},
"dependencies": {
"@headlessui/vue": "^1.7.9",

View File

@@ -149,13 +149,13 @@
<AppLogo class="w-12 -mb-4" />
x
</h2>
<p class="ml-1 text-lg text-base-content/50">Track, Organize, and Manage your Things.</p>
<p class="ml-1 text-lg text-base-content/50">{{ $t("tagline") }}</p>
</div>
<div class="flex mt-6 sm:mt-0 gap-4 ml-auto text-neutral-content">
<a class="tooltip" data-tip="Project Github" href="https://github.com/sysadminsmedia/homebox" target="_blank">
<MdiGithub class="h-8 w-8" />
</a>
<a href="https://twitter.com/haybytes" class="tooltip" data-tip="Follow The Developer" target="_blank">
<a href="https://noc.social/@sysadminsmedia" class="tooltip" data-tip="Follow The Developer" target="_blank">
<MdiTwitter class="h-8 w-8" />
</a>
<a href="https://discord.gg/aY4DCkpNA9" class="tooltip" data-tip="Join The Discord" target="_blank">
@@ -174,17 +174,17 @@
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Register
{{ $t("register") }}
</h2>
<FormTextField v-model="email" label="Set your email?" />
<FormTextField v-model="username" label="What's your name?" />
<FormTextField v-model="email" :label="$t('set_email')" />
<FormTextField v-model="username" :label="$t('set_name')" />
<div v-if="!(groupToken == '')" class="pt-4 pb-1 text-center">
<p>You're Joining an Existing Group!</p>
<p>{{ $t("joining_group") }}</p>
<button type="button" class="text-xs underline" @click="groupToken = ''">
Don't Want To Join a Group?
{{ $t("dont_join_group") }}
</button>
</div>
<FormPassword v-model="password" label="Set your password" />
<FormPassword v-model="password" :label="$t('set_password')" />
<PasswordScore v-model:valid="canRegister" :password="password" />
<div class="card-actions justify-end">
<button
@@ -193,7 +193,7 @@
:class="loading ? 'loading' : ''"
:disabled="loading || !canRegister"
>
Register
{{ $t("register") }}
</button>
</div>
</div>
@@ -204,17 +204,17 @@
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Login
{{ $t("login") }}
</h2>
<template v-if="status && status.demo">
<p class="text-xs italic text-center">This is a demo instance</p>
<p class="text-xs text-center"><b>Email</b> demo@example.com</p>
<p class="text-xs text-center"><b>Password</b> demo</p>
<p class="text-xs text-center"><b>{{ $t("email") }}</b> demo@example.com</p>
<p class="text-xs text-center"><b>{{ $t("password") }}</b> demo</p>
</template>
<FormTextField v-model="email" label="Email" />
<FormPassword v-model="loginPassword" label="Password" />
<FormTextField v-model="email" :label="$t('email')" />
<FormPassword v-model="loginPassword" :label="$t('password')" />
<div class="max-w-[140px]">
<FormCheckbox v-model="remember" label="Remember Me" />
<FormCheckbox v-model="remember" :label="$t('remember_me')" />
</div>
<div class="card-actions justify-end">
<button
@@ -241,18 +241,20 @@
<MdiLogin v-else class="w-5 h-5 swap-off" />
<MdiArrowRight class="w-5 h-5 swap-on" />
</template>
{{ registerForm ? "Login" : "Register" }}
{{ registerForm ? $t("login") : $t("register") }}
</BaseButton>
<p v-else class="text-base-content italic text-sm inline-flex items-center gap-2">
<MdiLock class="w-4 h-4 inline-block" />
Registration Disabled
{{ $t("disabled_registration") }}
</p>
</div>
</div>
</div>
</div>
<footer v-if="status" class="mt-auto text-center w-full bottom-0 pb-4">
<p class="text-center text-sm">Version: {{ status.build.version }} ~ Build: {{ status.build.commit }}</p>
<p class="text-center text-sm">
{{ $t("version") }}: {{ status.build.version }} ~ {{ $t("build") }}: {{ status.build.commit }}
</p>
</footer>
</div>
</template>

36
frontend/plugins/i18n.ts Normal file
View File

@@ -0,0 +1,36 @@
import type { CompileError, MessageCompiler, MessageContext } from "vue-i18n";
import { createI18n } from "vue-i18n";
import IntlMessageFormat from "intl-messageformat";
import messages from '@intlify/unplugin-vue-i18n/messages'
export default defineNuxtPlugin(({ vueApp }) => {
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: "en",
messageCompiler,
messages,
});
vueApp.use(i18n);
});
export const messageCompiler: MessageCompiler = (message, { locale, key, onError }) => {
if (typeof message === "string") {
/**
* You can tune your message compiler performance more with your cache strategy or also memoization at here
*/
const formatter = new IntlMessageFormat(message, locale);
return (ctx: MessageContext) => {
return formatter.format(ctx.values);
};
} else {
/**
* for AST.
* If you would like to support it,
* You need to transform locale messages such as `json`, `yaml`, etc. with the bundle plugin.
*/
onError && onError(new Error("not support for AST") as CompileError);
return () => key;
}
};

12656
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff