From ea4676d991e99f19b2c7efafe96032b9472da86f Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Wed, 25 Mar 2026 17:39:47 +0300 Subject: [PATCH] Finish i18n pass for menus and tool labels Localize remaining menu state labels like Hide/Show and Lock/Unlock, wire toolbar tool/action labels through the i18n stores, and localize the app menu UI toggle tooltip. Replace template-string JSON imports with an explicit locale loader map so published package builds have stable locale asset references. --- packages/vue/src/commands/useMenuModel.ts | 4 +- packages/vue/src/i18n/i18n.ts | 12 +++- packages/vue/src/i18n/messages.ts | 8 +++ packages/vue/src/locales/de.json | 8 +++ packages/vue/src/locales/es.json | 8 +++ packages/vue/src/locales/fr.json | 8 +++ packages/vue/src/locales/it.json | 8 +++ packages/vue/src/locales/pl.json | 8 +++ packages/vue/src/locales/ru.json | 8 +++ src/components/AppMenu.vue | 5 +- src/components/LayerTree.vue | 13 +++- src/components/Toolbar.vue | 78 +++++++++++++++-------- 12 files changed, 133 insertions(+), 35 deletions(-) diff --git a/packages/vue/src/commands/useMenuModel.ts b/packages/vue/src/commands/useMenuModel.ts index b32fd52b8..8f88f35b9 100644 --- a/packages/vue/src/commands/useMenuModel.ts +++ b/packages/vue/src/commands/useMenuModel.ts @@ -124,8 +124,8 @@ export function useMenuModel() { }) const selectionLabelMenu = computed(() => ({ - visibility: (editor.getSelectedNode()?.visible ?? true) ? 'Hide' : 'Show', - lock: (editor.getSelectedNode()?.locked ?? false) ? 'Unlock' : 'Lock' + visibility: (editor.getSelectedNode()?.visible ?? true) ? t.value.hide : t.value.show, + lock: (editor.getSelectedNode()?.locked ?? false) ? t.value.unlock : t.value.lock })) return { diff --git a/packages/vue/src/i18n/i18n.ts b/packages/vue/src/i18n/i18n.ts index 099401253..329df72f0 100644 --- a/packages/vue/src/i18n/i18n.ts +++ b/packages/vue/src/i18n/i18n.ts @@ -5,10 +5,20 @@ import { locale } from './locale' import type { Locale } from './locale' import type { ComponentsJSON } from '@nanostores/i18n' +const localeLoaders: Record, () => Promise<{ default: ComponentsJSON }>> = { + de: () => import('../locales/de.json'), + es: () => import('../locales/es.json'), + fr: () => import('../locales/fr.json'), + it: () => import('../locales/it.json'), + pl: () => import('../locales/pl.json'), + ru: () => import('../locales/ru.json') +} + export const i18n = createI18n(locale, { baseLocale: 'en', async get(code) { - const mod = (await import(`../locales/${code}.json`)) as { default: ComponentsJSON } + if (code === 'en') return {} + const mod = await localeLoaders[code]() return mod.default } }) diff --git a/packages/vue/src/i18n/messages.ts b/packages/vue/src/i18n/messages.ts index b34959d8d..3dcac5dc7 100644 --- a/packages/vue/src/i18n/messages.ts +++ b/packages/vue/src/i18n/messages.ts @@ -26,6 +26,14 @@ export const menuMessages = i18n('menu', { moveToPage: 'Move to page', createInstance: 'Create instance', + hide: 'Hide', + show: 'Show', + lock: 'Lock', + unlock: 'Unlock', + cut: 'Cut', + front: 'Front', + back: 'Back', + toggleUi: 'Toggle UI', bold: 'Bold', italic: 'Italic', diff --git a/packages/vue/src/locales/de.json b/packages/vue/src/locales/de.json index 5eb60cb0d..6a26f0139 100644 --- a/packages/vue/src/locales/de.json +++ b/packages/vue/src/locales/de.json @@ -19,6 +19,14 @@ "profiler": "Leistungsprofil", "moveToPage": "Auf Seite verschieben", "createInstance": "Instanz erstellen", + "hide": "Ausblenden", + "show": "Einblenden", + "lock": "Sperren", + "unlock": "Entsperren", + "cut": "Ausschneiden", + "front": "Nach vorn", + "back": "Nach hinten", + "toggleUi": "UI umschalten", "bold": "Fett", "italic": "Kursiv", "underline": "Unterstrichen" diff --git a/packages/vue/src/locales/es.json b/packages/vue/src/locales/es.json index a630fc279..bf1661dce 100644 --- a/packages/vue/src/locales/es.json +++ b/packages/vue/src/locales/es.json @@ -19,6 +19,14 @@ "profiler": "Perfil de rendimiento", "moveToPage": "Mover a página", "createInstance": "Crear instancia", + "hide": "Ocultar", + "show": "Mostrar", + "lock": "Bloquear", + "unlock": "Desbloquear", + "cut": "Cortar", + "front": "Al frente", + "back": "Al fondo", + "toggleUi": "Alternar interfaz", "bold": "Negrita", "italic": "Cursiva", "underline": "Subrayado" diff --git a/packages/vue/src/locales/fr.json b/packages/vue/src/locales/fr.json index fd6637b7d..27e528f3e 100644 --- a/packages/vue/src/locales/fr.json +++ b/packages/vue/src/locales/fr.json @@ -19,6 +19,14 @@ "profiler": "Profileur de performance", "moveToPage": "Déplacer vers la page", "createInstance": "Créer une instance", + "hide": "Masquer", + "show": "Afficher", + "lock": "Verrouiller", + "unlock": "Déverrouiller", + "cut": "Couper", + "front": "Premier plan", + "back": "Arrière-plan", + "toggleUi": "Basculer l’interface", "bold": "Gras", "italic": "Italique", "underline": "Souligné" diff --git a/packages/vue/src/locales/it.json b/packages/vue/src/locales/it.json index c33846614..39cce151a 100644 --- a/packages/vue/src/locales/it.json +++ b/packages/vue/src/locales/it.json @@ -19,6 +19,14 @@ "profiler": "Profilo prestazioni", "moveToPage": "Sposta nella pagina", "createInstance": "Crea istanza", + "hide": "Nascondi", + "show": "Mostra", + "lock": "Blocca", + "unlock": "Sblocca", + "cut": "Taglia", + "front": "Davanti", + "back": "Dietro", + "toggleUi": "Mostra/nascondi interfaccia", "bold": "Grassetto", "italic": "Corsivo", "underline": "Sottolineato" diff --git a/packages/vue/src/locales/pl.json b/packages/vue/src/locales/pl.json index 18b9f10d4..6bc344547 100644 --- a/packages/vue/src/locales/pl.json +++ b/packages/vue/src/locales/pl.json @@ -19,6 +19,14 @@ "profiler": "Profiler wydajności", "moveToPage": "Przenieś na stronę", "createInstance": "Utwórz instancję", + "hide": "Ukryj", + "show": "Pokaż", + "lock": "Zablokuj", + "unlock": "Odblokuj", + "cut": "Wytnij", + "front": "Do przodu", + "back": "Do tyłu", + "toggleUi": "Przełącz interfejs", "bold": "Pogrubienie", "italic": "Kursywa", "underline": "Podkreślenie" diff --git a/packages/vue/src/locales/ru.json b/packages/vue/src/locales/ru.json index f04d26f04..5870fd2a5 100644 --- a/packages/vue/src/locales/ru.json +++ b/packages/vue/src/locales/ru.json @@ -19,6 +19,14 @@ "profiler": "Профилирование", "moveToPage": "Переместить на страницу", "createInstance": "Создать экземпляр", + "hide": "Скрыть", + "show": "Показать", + "lock": "Заблокировать", + "unlock": "Разблокировать", + "cut": "Вырезать", + "front": "Вперёд", + "back": "Назад", + "toggleUi": "Переключить интерфейс", "bold": "Жирный", "italic": "Курсив", "underline": "Подчёркнутый" diff --git a/src/components/AppMenu.vue b/src/components/AppMenu.vue index 8ab8fa489..1fe7b7f0d 100644 --- a/src/components/AppMenu.vue +++ b/src/components/AppMenu.vue @@ -18,7 +18,7 @@ import IconChevronRight from '~icons/lucide/chevron-right' import { computed } from 'vue' -import { useInlineRename } from '@open-pencil/vue' +import { useI18n, useInlineRename } from '@open-pencil/vue' import { useMenuUI } from '@/components/ui/menu' import { IS_TAURI } from '@/constants' import { useAppMenu } from '@/composables/use-app-menu' @@ -46,6 +46,7 @@ function commitRename(input: HTMLInputElement) { const isMac = navigator.platform.includes('Mac') const mod = isMac ? '⌘' : 'Ctrl+' +const { menu: t } = useI18n() interface MenuAction { separator?: false @@ -91,7 +92,7 @@ const subMenuCls = useMenuUI({ content: 'min-w-44' }) @dblclick="startRename" >{{ store.state.documentName }} - +