mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-21 15:26:32 +03:00
build web with optional ui languages
This commit is contained in:
@@ -46,6 +46,38 @@ ${fileInfo.map((f) => `${INDENT}{"${f.uri}", "${f.mimeType}", ${f.variable}, ${f
|
|||||||
static constexpr size_t WWW_ASSETS_COUNT = sizeof(WWW_ASSETS) / sizeof(WWW_ASSETS[0]);
|
static constexpr size_t WWW_ASSETS_COUNT = sizeof(WWW_ASSETS) / sizeof(WWW_ASSETS[0]);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Optional locale allow-list, shared with the Vite build via VITE_APP_LOCALES
|
||||||
|
// (e.g. "en,de,nl"). When set, locale chunks outside the list are NOT embedded
|
||||||
|
// into firmware flash. `en` is always kept as the fallback. Unset => embed all.
|
||||||
|
const ALL_LOCALES = [
|
||||||
|
'cz',
|
||||||
|
'de',
|
||||||
|
'en',
|
||||||
|
'fr',
|
||||||
|
'it',
|
||||||
|
'nl',
|
||||||
|
'no',
|
||||||
|
'pl',
|
||||||
|
'sk',
|
||||||
|
'sv',
|
||||||
|
'tr'
|
||||||
|
];
|
||||||
|
const localeAllowList = (process.env.VITE_APP_LOCALES || '')
|
||||||
|
.split(',')
|
||||||
|
.map((locale) => locale.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const isExcludedLocaleChunk = (relativeFilePath) => {
|
||||||
|
if (localeAllowList.length === 0) return false;
|
||||||
|
const base = relativeFilePath.split(sep).pop();
|
||||||
|
const match = /^([a-z]{2})-[A-Za-z0-9_-]+\.js$/.exec(base);
|
||||||
|
if (!match) return false;
|
||||||
|
const code = match[1];
|
||||||
|
// Only treat known locale codes as locale chunks; never drop the en fallback.
|
||||||
|
if (!ALL_LOCALES.includes(code) || code === 'en') return false;
|
||||||
|
return !localeAllowList.includes(code);
|
||||||
|
};
|
||||||
|
|
||||||
const getFilesSync = (dir, files = []) => {
|
const getFilesSync = (dir, files = []) => {
|
||||||
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
||||||
const entryPath = resolve(dir, entry.name);
|
const entryPath = resolve(dir, entry.name);
|
||||||
@@ -116,7 +148,12 @@ writeStream.write(ARDUINO_INCLUDES);
|
|||||||
|
|
||||||
const buildPath = resolve(sourcePath);
|
const buildPath = resolve(sourcePath);
|
||||||
for (const filePath of getFilesSync(buildPath)) {
|
for (const filePath of getFilesSync(buildPath)) {
|
||||||
writeFile(relative(buildPath, filePath), readFileSync(filePath));
|
const relativeFilePath = relative(buildPath, filePath);
|
||||||
|
if (isExcludedLocaleChunk(relativeFilePath)) {
|
||||||
|
console.log(`Skipping locale (not in VITE_APP_LOCALES): ${relativeFilePath}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
writeFile(relativeFilePath, readFileSync(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
writeStream.write(generateWWWClass());
|
writeStream.write(generateWWWClass());
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { Locales } from 'i18n/i18n-types';
|
|||||||
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
||||||
import { detectLocale, navigatorDetector } from 'typesafe-i18n/detectors';
|
import { detectLocale, navigatorDetector } from 'typesafe-i18n/detectors';
|
||||||
|
|
||||||
const AVAILABLE_LOCALES = [
|
const ALL_LOCALES = [
|
||||||
'de',
|
'de',
|
||||||
'en',
|
'en',
|
||||||
'it',
|
'it',
|
||||||
@@ -23,6 +23,20 @@ const AVAILABLE_LOCALES = [
|
|||||||
'cz'
|
'cz'
|
||||||
] as Locales[];
|
] as Locales[];
|
||||||
|
|
||||||
|
// Optional build-time allow-list (e.g. VITE_APP_LOCALES="en,de,nl"). When unset,
|
||||||
|
// every locale is available. `en` is always kept as the fallback locale, and the
|
||||||
|
// progmem generator embeds the matching subset into firmware flash.
|
||||||
|
const localeAllowList = (import.meta.env.VITE_APP_LOCALES ?? '')
|
||||||
|
.split(',')
|
||||||
|
.map((locale) => locale.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const AVAILABLE_LOCALES: Locales[] = localeAllowList.length
|
||||||
|
? ALL_LOCALES.filter(
|
||||||
|
(locale) => locale === 'en' || localeAllowList.includes(locale)
|
||||||
|
)
|
||||||
|
: ALL_LOCALES;
|
||||||
|
|
||||||
const App = memo(() => {
|
const App = memo(() => {
|
||||||
const [wasLoaded, setWasLoaded] = useState(false);
|
const [wasLoaded, setWasLoaded] = useState(false);
|
||||||
const [locale, setLocale] = useState<Locales>('en');
|
const [locale, setLocale] = useState<Locales>('en');
|
||||||
@@ -30,7 +44,12 @@ const App = memo(() => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeLocale = async () => {
|
const initializeLocale = async () => {
|
||||||
const browserLocale = detectLocale('en', AVAILABLE_LOCALES, navigatorDetector);
|
const browserLocale = detectLocale('en', AVAILABLE_LOCALES, navigatorDetector);
|
||||||
const newLocale = (localStorage.getItem('lang') || browserLocale) as Locales;
|
const stored = localStorage.getItem('lang');
|
||||||
|
// Ignore a stored locale that isn't available (e.g. trimmed from this build).
|
||||||
|
const newLocale =
|
||||||
|
stored && AVAILABLE_LOCALES.includes(stored as Locales)
|
||||||
|
? (stored as Locales)
|
||||||
|
: browserLocale;
|
||||||
localStorage.setItem('lang', newLocale);
|
localStorage.setItem('lang', newLocale);
|
||||||
setLocale(newLocale);
|
setLocale(newLocale);
|
||||||
await loadLocaleAsync(newLocale);
|
await loadLocaleAsync(newLocale);
|
||||||
|
|||||||
12
interface/src/vite-env.d.ts
vendored
12
interface/src/vite-env.d.ts
vendored
@@ -1 +1,13 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
// Optional comma-separated allow-list of locales to ship (e.g. "en,de,nl").
|
||||||
|
// Unset => all locales are bundled and embedded. `en` is always kept as the
|
||||||
|
// fallback. Consumed by App.tsx (UI language list) and progmem-generator.js
|
||||||
|
// (which locale chunks get embedded into firmware flash).
|
||||||
|
readonly VITE_APP_LOCALES?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user