diff --git a/app.vue b/app.vue index f8466dc..ba0a63b 100644 --- a/app.vue +++ b/app.vue @@ -42,7 +42,7 @@ import { defineComponent } from "vue"; import type { RouteLocationNormalizedLoaded } from "vue-router"; import ToolboxHelper from "./classes/ToolboxHelper"; import { useAppStore } from "~/stores/useAppStore"; -import data from "data/test.json"; +// import data from "data/test.json"; export default defineComponent({ name: "IndexPage", @@ -54,7 +54,7 @@ export default defineComponent({ // adminModalId: string; // modal: Modal; // adminModal: Modal; - data: typeof data; + // data: typeof data; // selectedItem: Item | null; // konami: KonamiCode; // contacts: Array; @@ -65,7 +65,7 @@ export default defineComponent({ route: useRoute(), // modalId: "productModal", // adminModalId: "adminModal", - data, + // data, // selectedItem: null, // modal: {} as Modal, // adminModal: {} as Modal, @@ -97,11 +97,11 @@ export default defineComponent({ }, mounted() { // * Make sure array does not start using 0 index - const items: typeof data.items = []; - this.data.items.forEach((item, itemIndex) => { - items[itemIndex + 1] = item; - }); - this.data.items = items; + // const items: typeof data.items = []; + // this.data.items.forEach((item, itemIndex) => { + // items[itemIndex + 1] = item; + // }); + // this.data.items = items; // * Init contacts (Fetch form local storage or new) // const serializedContacts = window.localStorage.getItem("contacts"); // this.contacts = !serializedContacts ? new Array() : JSON.parse(serializedContacts); @@ -121,7 +121,7 @@ export default defineComponent({ // * INIT STATES window.ipcRendererObject.getJsonData().then((dataJson) => { - useAppStore().setupAppData(dataJson); + useAppStore().setupAppData(dataJson, this.$i18n.locale, String(this.$i18n.fallbackLocale)); this.loading = false; this.setTimeout(); dataJson.fonts.forEach(font => this.injectFontFace(font.font_name, font.font)); diff --git a/classes/AppState.ts b/classes/AppState.ts index 4df50ea..68777dc 100644 --- a/classes/AppState.ts +++ b/classes/AppState.ts @@ -4,14 +4,36 @@ export default class AppState { /** Json data as exported from zip and compliant with schemas */ private jsonData: TypeApp; - constructor(jsonData: TypeApp) { + /** Current App locale */ + private locale: string; + /** Fallback App locale */ + private fallbackLocale: string; + + constructor(jsonData: TypeApp, locale: string, fallbackLocale: string) { this.jsonData = jsonData; + this.locale = locale; + this.fallbackLocale = fallbackLocale; } public settings(): TypeSettings { return this.jsonData.settings; } + public switchLocale(locale: string): void { + this.locale = locale; + } + + public geti18N() { + return { + locale: this.locale, + fallbackLocale: this.fallbackLocale, + }; + } + + public getFallbackLocale(): string { + return this.fallbackLocale; + } + // * --- BRANDS public brands(): Array { @@ -19,7 +41,7 @@ export default class AppState { } public brand(slug:string): TypeBrand|undefined { - return this.jsonData.items.find(brand => brand.slug === slug); + return this.jsonData.items.find(brand => brand.slug[this.locale] === slug); } public brandFromRange(range:TypeRange): TypeBrand { @@ -48,7 +70,7 @@ export default class AppState { } public range(slug:string): TypeRange|undefined { - return this.ranges().find(range => range.slug === slug); + return this.ranges().find(range => range.slug[this.locale] === slug); } // * --- END RANGES diff --git a/classes/ToolboxHelper.ts b/classes/ToolboxHelper.ts index e4eb67d..76f0c67 100644 --- a/classes/ToolboxHelper.ts +++ b/classes/ToolboxHelper.ts @@ -32,7 +32,7 @@ export default class ToolboxHelper { } public static trans(text: string, params: Record = {}, localeToUse: string|null = null): string { - const { t } = useI18n(); + const { t } = useI18n({ useScope: "global"}); if(localeToUse) { return t(text, params, { locale: localeToUse, @@ -40,4 +40,15 @@ export default class ToolboxHelper { } return t(text, params); } + + public static getTranslatableField(model: TypeBrand | TypeRange | TypeProduct, field: "name" | "slug" | "description"): string { + const { locale, fallbackLocale } = useAppStore().getI18n(); + const i18nField: Typei18nField | null = (model[field as keyof typeof model] ?? null) as Typei18nField | null; + + if (!i18nField) { + throw new Error(`getTranslatableField ${field} is not part of ${model}`); + } + + return i18nField[locale] !== undefined ? i18nField[locale] : i18nField[fallbackLocale]; + } } diff --git a/components/NavigationMenu.vue b/components/NavigationMenu.vue index 7e670e2..adcb900 100644 --- a/components/NavigationMenu.vue +++ b/components/NavigationMenu.vue @@ -5,7 +5,7 @@ @@ -117,6 +117,9 @@ export default defineComponent({ t(text: string, params: Record = {}, localeToUse: string|null = null): string { return ToolboxHelper.trans(text, params, localeToUse); }, + transField(model: TypeBrand | TypeRange | TypeProduct, field: "name" | "slug" | "description") { + return ToolboxHelper.getTranslatableField(model, field); + }, titleAnimations(rangeIndex: number) { const animation = this.animations.at(ToolboxHelper.randomInteger(0, this.animations.length -1)); const out: Map = new Map(); @@ -144,7 +147,7 @@ export default defineComponent({ }, gotoRange(range: TypeRange) { useAppStore().setActiveRange(range); - navigateTo(useLocaleRoute()({"name": "ranges-slug", "params": {"slug": range.slug}})); + navigateTo(useLocaleRoute()({"name": "ranges-slug", "params": {"slug": this.transField(range, "slug")}})); } } }); diff --git a/pages/index.vue b/pages/index.vue index cb66130..b505e04 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -28,11 +28,11 @@ :class="{...{'brand-title fw-bold text-capitalize fs-1 animate__animated': true}, ...titleAnimations(brandIndex)}" :style="{ 'color': brand.color }" role="button" - :title="t('view_brand_ranges', {'brand_name': brand.name})" + :title="t('view_brand_ranges', {'brand_name': transField(brand, 'name')})" data-bs-toggle="tooltip" @click="gotoBrand(brand)" > - {{ brand.name }} + {{ transField(brand, 'name') }} @@ -46,15 +46,15 @@
@@ -114,6 +114,9 @@ export default defineComponent({ t(text: string, params: Record = {}, localeToUse: string|null = null): string { return ToolboxHelper.trans(text, params, localeToUse); }, + transField(model: TypeBrand | TypeRange | TypeProduct, field: "name" | "slug" | "description") { + return ToolboxHelper.getTranslatableField(model, field); + }, setBrandToAnimate(): void { clearTimeout(this.animateTimeout); this.animateIndex = ToolboxHelper.randomInteger(0, this.appState.brands().length -1); @@ -127,7 +130,7 @@ export default defineComponent({ }, gotoBrand(brand: TypeBrand) { useAppStore().setActiveBrand(brand); - navigateTo(useLocaleRoute()({"name": "brands-slug", "params": {"slug": brand.slug}})); + navigateTo(useLocaleRoute()({"name": "brands-slug", "params": {"slug": this.transField(brand, "slug")}})); } }, }); diff --git a/pages/ranges/[slug].vue b/pages/ranges/[slug].vue index 48d0231..5635612 100644 --- a/pages/ranges/[slug].vue +++ b/pages/ranges/[slug].vue @@ -44,13 +44,13 @@ 'img-fluid': activeProduct?.id === product.id, }" :src="`/data/${product.picture}`" - :alt="t('products_logo', {'product_name': product.name})" - :title="t('products_logo', {'product_name': product.name})" + :alt="t('products_logo', {'product_name': transField(product, 'name')})" + :title="t('products_logo', {'product_name': transField(product, 'name')})" >
-
+
@@ -69,7 +69,7 @@ 'background-color': activeProduct?.id === product.id ? '' : range.color }" > - {{ product.name }} + {{ transField(product, 'name') }}
@@ -133,6 +133,7 @@ export default defineComponent({ return ; } this.range = range; + // * Pour que ces putains de titres label aient la meme hauteur. this.$nextTick(() => { const spanList = (this.$refs.rangeList as HTMLDivElement).querySelectorAll(".product-title") as NodeListOf; @@ -152,6 +153,9 @@ export default defineComponent({ t(text: string, params: Record = {}, localeToUse: string|null = null): string { return ToolboxHelper.trans(text, params, localeToUse); }, + transField(model: TypeBrand | TypeRange | TypeProduct, field: "name" | "slug" | "description") { + return ToolboxHelper.getTranslatableField(model, field); + }, getContrastYIQ(hexcolor: string): string { return ToolboxHelper.getContrastYIQ(hexcolor); }, diff --git a/schemas/brand.schema.json b/schemas/brand.schema.json index fc92887..b96f04f 100644 --- a/schemas/brand.schema.json +++ b/schemas/brand.schema.json @@ -11,11 +11,11 @@ }, "name": { "description": "Nom de la marque", - "type": "string" + "type": "object" }, "slug": { "description": "Slug de la marque", - "type": "string" + "type": "object" }, "color": { "description": "Couleur de la marque", diff --git a/schemas/product.schema.json b/schemas/product.schema.json index b014525..77022f3 100644 --- a/schemas/product.schema.json +++ b/schemas/product.schema.json @@ -11,15 +11,15 @@ }, "name": { "description": "Nom du produit", - "type": "string" + "type": "object" }, "slug": { "description": "Slug du produit", - "type": "string" + "type": "object" }, "description": { "description": "Description du produit, texte WYSYWYG", - "type": "string" + "type": "object" }, "picture": { "description": "Logo du produit, chemin tel que présent dans le zip", diff --git a/schemas/range.schema.json b/schemas/range.schema.json index 9500ebf..f49837e 100644 --- a/schemas/range.schema.json +++ b/schemas/range.schema.json @@ -11,11 +11,11 @@ }, "name": { "description": "Nom de la gamme", - "type": "string" + "type": "object" }, "slug": { "description": "Slug de la gamme", - "type": "string" + "type": "object" }, "picture": { "description": "Logo de la gamme, chemin tel que présent dans le zip", diff --git a/stores/useAppStore.ts b/stores/useAppStore.ts index 4bd7366..db8cb05 100644 --- a/stores/useAppStore.ts +++ b/stores/useAppStore.ts @@ -2,7 +2,7 @@ import AppState from "../classes/AppState"; export const useAppStore = defineStore("appStore", { state: () => ({ - data: new AppState({} as TypeApp), + data: new AppState({} as TypeApp, "fr", "fr"), activeBrand: null, activeRange: null, } as { @@ -11,8 +11,8 @@ export const useAppStore = defineStore("appStore", { activeRange: TypeRange | null; }), actions: { - setupAppData(dataJson: TypeApp): void { - this.data = new AppState(dataJson); + setupAppData(dataJson: TypeApp, locale: string, fallbackLocale: string): void { + this.data = new AppState(dataJson, locale, fallbackLocale); }, setActiveBrand(brand: TypeBrand|null): void { this.activeBrand = brand; @@ -47,6 +47,12 @@ export const useAppStore = defineStore("appStore", { return null; } return font; + }, + getI18n() { + return this.data.geti18N(); + }, + switchLocale(locale: string): void { + this.data.switchLocale(locale); } } });