refactor: extract i18n stuff into its own package

Signed-off-by: Fernando Fernández <ferferga@hotmail.com>
This commit is contained in:
Fernando Fernández
2025-04-29 20:05:38 +02:00
parent d668199a54
commit ff24e6616f
55 changed files with 155 additions and 39 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
"i18n-ally.sortKeys": true,
"i18n-ally.sourceLanguage": "en",
"i18n-ally.localesPaths": [
"frontend/locales"
"packages/i18n/strings"
],
"i18n-ally.enabledFrameworks": [
"vue"
+3
View File
@@ -29,6 +29,7 @@
},
"dependencies": {
"@fontsource-variable/figtree": "5.2.6",
"@jellyfin-vue/i18n": "*",
"@jellyfin-vue/shared": "*",
"@jellyfin-vue/ui-toolkit": "*",
"@jellyfin/sdk": "0.11.0",
@@ -44,6 +45,8 @@
"destr": "2.0.5",
"fast-equals": "5.2.2",
"hls.js": "1.6.0",
"i18next": "25.0.1",
"i18next-vue": "5.2.0",
"libpgs": "0.8.1",
"sortablejs": "1.15.6",
"swiper": "11.2.6",
-17
View File
@@ -1,17 +0,0 @@
import { createI18n } from 'vue-i18n';
import messages from '@intlify/unplugin-vue-i18n/messages';
/**
* See #/store/clientSettings to check where the current user language is initialised
*/
const DEFAULT_LANGUAGE = 'en';
export const vuePlugin = createI18n({
fallbackLocale: DEFAULT_LANGUAGE,
globalInjection: true,
legacy: false,
messages: messages
});
export const i18n = vuePlugin.global;
-6
View File
@@ -30,12 +30,6 @@ declare module 'vue' {
}
}
declare module 'vue-i18n' {
type messages = typeof en;
export interface DefineLocaleMessage extends messages {}
}
/**
* This is important: https://stackoverflow.com/a/64189046
* https://www.typescriptlang.org/docs/handbook/modules.html
-10
View File
@@ -1,5 +1,4 @@
import { resolve } from 'node:path';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import Virtual from '@rollup/plugin-virtual';
import VueDevTools from 'vite-plugin-vue-devtools';
import Vue from '@vitejs/plugin-vue';
@@ -13,7 +12,6 @@ import { defineConfig } from 'vite';
import { JBundle, JMonorepo } from '@jellyfin-vue/vite-plugins';
import { JellyfinVueUIToolkit } from '@jellyfin-vue/ui-toolkit/resolver';
import virtualModules from './scripts/virtual-modules';
import { localeFilesFolder } from './scripts/paths';
export default defineConfig({
appType: 'spa',
@@ -54,14 +52,6 @@ export default defineConfig({
JellyfinVueUIToolkit()
]
}),
VueI18nPlugin({
runtimeOnly: true,
compositionOnly: true,
fullInstall: false,
forceStringify: true,
include: localeFilesFolder,
dropMessageCompiler: true
}),
UnoCSS(),
VueDevTools()
],
+60 -5
View File
@@ -32,6 +32,7 @@
"version": "0.3.1",
"dependencies": {
"@fontsource-variable/figtree": "5.2.6",
"@jellyfin-vue/i18n": "*",
"@jellyfin-vue/shared": "*",
"@jellyfin-vue/ui-toolkit": "*",
"@jellyfin/sdk": "0.11.0",
@@ -47,6 +48,8 @@
"destr": "2.0.5",
"fast-equals": "5.2.2",
"hls.js": "1.6.0",
"i18next": "25.0.1",
"i18next-vue": "5.2.0",
"libpgs": "0.8.1",
"sortablejs": "1.15.6",
"swiper": "11.2.6",
@@ -563,10 +566,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==",
"dev": true,
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
@@ -1455,6 +1457,10 @@
"resolved": "frontend",
"link": true
},
"node_modules/@jellyfin-vue/i18n": {
"resolved": "packages/i18n",
"link": true
},
"node_modules/@jellyfin-vue/shared": {
"resolved": "packages/shared",
"link": true
@@ -7666,6 +7672,47 @@
"node": ">=18.18.0"
}
},
"node_modules/i18next": {
"version": "25.0.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.1.tgz",
"integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.10"
},
"peerDependencies": {
"typescript": "^5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/i18next-vue": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/i18next-vue/-/i18next-vue-5.2.0.tgz",
"integrity": "sha512-VlP2BoEhBQkjr8v0e/TZ3o+3WCExxEbEDYHTEv3FpGZbr6idnIUBYu0NWKG+Sv7f1Xxp78qAR7fM5vy2pRJ18A==",
"license": "MIT",
"peerDependencies": {
"i18next": ">=23",
"vue": "^3.4.38"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -10150,7 +10197,6 @@
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true,
"license": "MIT"
},
"node_modules/regexp-ast-analysis": {
@@ -12551,6 +12597,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"packages/i18n": {
"name": "@jellyfin-vue/i18n",
"devDependencies": {
"@jellyfin-vue/configs": "*"
},
"peerDependencies": {
"i18next": "25.0.1"
}
},
"packages/shared": {
"name": "@jellyfin-vue/shared",
"dependencies": {
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'eslint/config';
import { getBaseConfig, getTSVueConfig } from '@jellyfin-vue/configs/lint';
import pkg from './package.json' with { type: 'json' };
export default defineConfig([
...getBaseConfig(pkg.name),
...getTSVueConfig(false, import.meta.dirname)
]);
+17
View File
@@ -0,0 +1,17 @@
import i18next, { type ResourceLanguage } from 'i18next';
/**
* In @jellyfin-vue/frontend, see #/store/clientSettings to check where the current user language is initialised
*/
const DEFAULT_LANGUAGE = 'en';
const resources = import.meta.glob<ResourceLanguage>('/strings/*.json', { eager: true });
await i18next.init({
fallbackLng: DEFAULT_LANGUAGE,
lng: DEFAULT_LANGUAGE,
interpolation: {
escapeValue: false
},
resources
});
+25
View File
@@ -0,0 +1,25 @@
{
"name": "@jellyfin-vue/i18n",
"type": "module",
"description": "All internacionalization files for Jellyfin Vue",
"main": "./index.ts",
"types": "./types.d.ts",
"exports": {
".": {
"types": "./types.d.ts",
"default": "./index.ts"
}
},
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint:inspect": "eslint-config-inspector",
"check:types": "vue-tsc"
},
"devDependencies": {
"@jellyfin-vue/configs": "*"
},
"peerDependencies": {
"i18next": "25.0.1"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"extends": "@jellyfin-vue/configs/typescript",
"compilerOptions": {
"baseUrl": ".",
"lib": [
"ESNext",
"DOM"
],
"types": ["vite/client"]
},
"exclude": [
"dist",
"node_modules",
"coverage"
],
"include": [
"*.d.ts",
"*.ts",
"**/*.json"
],
}
+20
View File
@@ -0,0 +1,20 @@
import type en from './strings/en.json';
type FallbackLanguage = typeof en;
declare module 'i18next' {
interface CustomTypeOptions {
resources: {
en: FallbackLanguage;
[key: string]: FallbackLanguage;
};
strictKeyChecks: true;
}
}
/**
* This is important: https://stackoverflow.com/a/64189046
* https://www.typescriptlang.org/docs/handbook/modules.html
*/
export { };