waybetter 0.1.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -6
- package/bin/waybetter.mjs +43 -0
- package/build/runtime.mjs +105 -0
- package/cli/buildenv.mjs +130 -0
- package/cli/dev.mjs +97 -0
- package/cli/i18n.mjs +382 -0
- package/cli/manifests.mjs +383 -0
- package/cli/release.mjs +437 -0
- package/cli/releasecheck.mjs +15 -0
- package/cli/remote.mjs +260 -0
- package/cli/store.mjs +378 -0
- package/cli/strings.mjs +123 -0
- package/dist/runtime/column.d.ts +93 -0
- package/dist/runtime/column.js +451 -0
- package/dist/runtime/column.js.map +1 -0
- package/dist/runtime/commandReport.d.ts +19 -0
- package/dist/runtime/commandReport.js +47 -0
- package/dist/runtime/commandReport.js.map +1 -0
- package/dist/runtime/commands.d.ts +72 -0
- package/dist/runtime/commands.js +214 -0
- package/dist/runtime/commands.js.map +1 -0
- package/dist/runtime/devErrors.d.ts +16 -0
- package/dist/runtime/devErrors.js +30 -0
- package/dist/runtime/devErrors.js.map +1 -0
- package/dist/runtime/domHub.d.ts +21 -0
- package/dist/runtime/domHub.js +139 -4
- package/dist/runtime/domHub.js.map +1 -1
- package/dist/runtime/featureModules.d.ts +157 -0
- package/dist/runtime/featureModules.js +484 -0
- package/dist/runtime/featureModules.js.map +1 -0
- package/dist/runtime/featureTable.d.ts +144 -0
- package/dist/runtime/featureTable.js +223 -0
- package/dist/runtime/featureTable.js.map +1 -0
- package/dist/runtime/fonts.d.ts +22 -0
- package/dist/runtime/fonts.js +72 -0
- package/dist/runtime/fonts.js.map +1 -0
- package/dist/runtime/i18n.d.ts +138 -0
- package/dist/runtime/i18n.js +614 -0
- package/dist/runtime/i18n.js.map +1 -0
- package/dist/runtime/i18nCheck.d.ts +30 -0
- package/dist/runtime/i18nCheck.js +224 -0
- package/dist/runtime/i18nCheck.js.map +1 -0
- package/dist/runtime/pageLang.d.ts +8 -0
- package/dist/runtime/pageLang.js +64 -0
- package/dist/runtime/pageLang.js.map +1 -0
- package/dist/runtime/pageStrings.d.ts +35 -0
- package/dist/runtime/pageStrings.js +76 -0
- package/dist/runtime/pageStrings.js.map +1 -0
- package/dist/runtime/panelButton.d.ts +33 -0
- package/dist/runtime/panelButton.js +83 -0
- package/dist/runtime/panelButton.js.map +1 -0
- package/dist/runtime/permissions.d.ts +47 -0
- package/dist/runtime/permissions.js +141 -0
- package/dist/runtime/permissions.js.map +1 -0
- package/dist/runtime/pluginApi.d.ts +51 -2
- package/dist/runtime/pluginApi.js +137 -22
- package/dist/runtime/pluginApi.js.map +1 -1
- package/dist/runtime/pluginI18n.d.ts +29 -0
- package/dist/runtime/pluginI18n.js +119 -0
- package/dist/runtime/pluginI18n.js.map +1 -0
- package/dist/runtime/settingsForm.d.ts +66 -0
- package/dist/runtime/settingsForm.js +114 -0
- package/dist/runtime/settingsForm.js.map +1 -0
- package/dist/runtime/site.d.ts +5 -0
- package/dist/runtime/site.js +1 -1
- package/dist/runtime/site.js.map +1 -1
- package/dist/runtime/siteRuntime.d.ts +144 -0
- package/dist/runtime/siteRuntime.js +175 -0
- package/dist/runtime/siteRuntime.js.map +1 -0
- package/dist/runtime/stringPackHook.d.ts +84 -0
- package/dist/runtime/stringPackHook.js +220 -0
- package/dist/runtime/stringPackHook.js.map +1 -0
- package/dist/runtime/stringPackTranslate.d.ts +61 -0
- package/dist/runtime/stringPackTranslate.js +267 -0
- package/dist/runtime/stringPackTranslate.js.map +1 -0
- package/dist/runtime/stringPacks.d.ts +248 -0
- package/dist/runtime/stringPacks.js +610 -0
- package/dist/runtime/stringPacks.js.map +1 -0
- package/dist/runtime/styles.d.ts +15 -0
- package/dist/runtime/styles.js +81 -0
- package/dist/runtime/styles.js.map +1 -0
- package/dist/runtime/textNet.d.ts +15 -0
- package/dist/runtime/textNet.js +131 -0
- package/dist/runtime/textNet.js.map +1 -0
- package/dist/runtime/themeApi.d.ts +121 -0
- package/dist/runtime/themeApi.js +220 -0
- package/dist/runtime/themeApi.js.map +1 -0
- package/dist/runtime/themeBar.d.ts +28 -0
- package/dist/runtime/themeBar.js +82 -0
- package/dist/runtime/themeBar.js.map +1 -0
- package/dist/runtime/themeChoice.d.ts +52 -0
- package/dist/runtime/themeChoice.js +69 -0
- package/dist/runtime/themeChoice.js.map +1 -0
- package/dist/runtime/themeComponents.d.ts +435 -0
- package/dist/runtime/themeComponents.js +476 -0
- package/dist/runtime/themeComponents.js.map +1 -0
- package/dist/runtime/themeDecor.d.ts +73 -0
- package/dist/runtime/themeDecor.js +133 -0
- package/dist/runtime/themeDecor.js.map +1 -0
- package/dist/runtime/themeDerive.d.ts +69 -0
- package/dist/runtime/themeDerive.js +117 -0
- package/dist/runtime/themeDerive.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +52 -0
- package/dist/runtime/themeDoctor.js +112 -0
- package/dist/runtime/themeDoctor.js.map +1 -0
- package/dist/runtime/themeGuards.d.ts +60 -0
- package/dist/runtime/themeGuards.js +235 -0
- package/dist/runtime/themeGuards.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +130 -0
- package/dist/runtime/themeIcons.js +271 -0
- package/dist/runtime/themeIcons.js.map +1 -0
- package/dist/runtime/themeInk.d.ts +37 -0
- package/dist/runtime/themeInk.js +111 -0
- package/dist/runtime/themeInk.js.map +1 -0
- package/dist/runtime/themeInvariants.d.ts +39 -0
- package/dist/runtime/themeInvariants.js +273 -0
- package/dist/runtime/themeInvariants.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +114 -0
- package/dist/runtime/themeRuntime.js +570 -0
- package/dist/runtime/themeRuntime.js.map +1 -0
- package/dist/runtime/themeSchema.d.ts +10 -0
- package/dist/runtime/themeSchema.js +150 -0
- package/dist/runtime/themeSchema.js.map +1 -0
- package/dist/runtime/themeSpec.d.ts +263 -0
- package/dist/runtime/themeSpec.js +435 -0
- package/dist/runtime/themeSpec.js.map +1 -0
- package/dist/runtime/themeTokens.d.ts +12 -0
- package/dist/runtime/themeTokens.js +14 -6
- package/dist/runtime/themeTokens.js.map +1 -1
- package/dist/runtime/themeVariants.d.ts +30 -0
- package/dist/runtime/themeVariants.js +53 -0
- package/dist/runtime/themeVariants.js.map +1 -0
- package/dist/runtime/ui.d.ts +192 -0
- package/dist/runtime/ui.js +926 -0
- package/dist/runtime/ui.js.map +1 -0
- package/dist/shell/InstallDialog.d.ts +40 -0
- package/dist/shell/InstallDialog.js +176 -0
- package/dist/shell/InstallDialog.js.map +1 -0
- package/dist/shell/StoreView.d.ts +78 -0
- package/dist/shell/StoreView.js +108 -0
- package/dist/shell/StoreView.js.map +1 -0
- package/dist/shell/addressModel.js.map +1 -1
- package/dist/shell/chrome.js.map +1 -1
- package/dist/shell/deckModel.d.ts +117 -0
- package/dist/shell/deckModel.js +267 -0
- package/dist/shell/deckModel.js.map +1 -0
- package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
- package/dist/shell/frame/AccountSwitcher.js +243 -0
- package/dist/shell/frame/AccountSwitcher.js.map +1 -0
- package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
- package/dist/shell/frame/AddColumnDialog.js +81 -0
- package/dist/shell/frame/AddColumnDialog.js.map +1 -0
- package/dist/shell/frame/AppMenu.d.ts +44 -0
- package/dist/shell/frame/AppMenu.js +721 -0
- package/dist/shell/frame/AppMenu.js.map +1 -0
- package/dist/shell/frame/CommandPalette.d.ts +7 -0
- package/dist/shell/frame/CommandPalette.js +118 -0
- package/dist/shell/frame/CommandPalette.js.map +1 -0
- package/dist/shell/frame/DeckView.d.ts +13 -0
- package/dist/shell/frame/DeckView.js +287 -0
- package/dist/shell/frame/DeckView.js.map +1 -0
- package/dist/shell/frame/FindBar.d.ts +10 -0
- package/dist/shell/frame/FindBar.js +108 -0
- package/dist/shell/frame/FindBar.js.map +1 -0
- package/dist/shell/frame/MediaPanel.d.ts +8 -0
- package/dist/shell/frame/MediaPanel.js +76 -0
- package/dist/shell/frame/MediaPanel.js.map +1 -0
- package/dist/shell/frame/NewTabPage.d.ts +9 -0
- package/dist/shell/frame/NewTabPage.js +71 -0
- package/dist/shell/frame/NewTabPage.js.map +1 -0
- package/dist/shell/frame/PaneTheme.d.ts +16 -0
- package/dist/shell/frame/PaneTheme.js +67 -0
- package/dist/shell/frame/PaneTheme.js.map +1 -0
- package/dist/shell/frame/ReportDialog.d.ts +3 -0
- package/dist/shell/frame/ReportDialog.js +95 -0
- package/dist/shell/frame/ReportDialog.js.map +1 -0
- package/dist/shell/frame/Shell.d.ts +9 -0
- package/dist/shell/frame/Shell.js +1139 -0
- package/dist/shell/frame/Shell.js.map +1 -0
- package/dist/shell/frame/Titlebar.d.ts +17 -0
- package/dist/shell/frame/Titlebar.js +79 -0
- package/dist/shell/frame/Titlebar.js.map +1 -0
- package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
- package/dist/shell/frame/TitlebarExtras.js +202 -0
- package/dist/shell/frame/TitlebarExtras.js.map +1 -0
- package/dist/shell/frame/WebPanel.d.ts +4 -0
- package/dist/shell/frame/WebPanel.js +185 -0
- package/dist/shell/frame/WebPanel.js.map +1 -0
- package/dist/shell/frame/accountModel.d.ts +49 -0
- package/dist/shell/frame/accountModel.js +67 -0
- package/dist/shell/frame/accountModel.js.map +1 -0
- package/dist/shell/frame/accountSwitcher.css +91 -0
- package/dist/shell/frame/appI18n.d.ts +44 -0
- package/dist/shell/frame/appI18n.js +64 -0
- package/dist/shell/frame/appI18n.js.map +1 -0
- package/dist/shell/frame/appMenu.css +127 -0
- package/dist/shell/frame/backupModel.d.ts +38 -0
- package/dist/shell/frame/backupModel.js +192 -0
- package/dist/shell/frame/backupModel.js.map +1 -0
- package/dist/shell/frame/boot.d.ts +8 -0
- package/dist/shell/frame/boot.js +37 -0
- package/dist/shell/frame/boot.js.map +1 -0
- package/dist/shell/frame/brand.d.ts +1 -0
- package/dist/shell/frame/brand.js +4 -0
- package/dist/shell/frame/brand.js.map +1 -0
- package/dist/shell/frame/bridge.d.ts +35 -0
- package/dist/shell/frame/bridge.js +70 -0
- package/dist/shell/frame/bridge.js.map +1 -0
- package/dist/shell/frame/chromeTheme.d.ts +47 -0
- package/dist/shell/frame/chromeTheme.js +118 -0
- package/dist/shell/frame/chromeTheme.js.map +1 -0
- package/dist/shell/frame/deck.css +209 -0
- package/dist/shell/frame/deckStore.d.ts +89 -0
- package/dist/shell/frame/deckStore.js +239 -0
- package/dist/shell/frame/deckStore.js.map +1 -0
- package/dist/shell/frame/devHost.d.ts +35 -0
- package/dist/shell/frame/devHost.js +80 -0
- package/dist/shell/frame/devHost.js.map +1 -0
- package/dist/shell/frame/devModel.d.ts +7 -0
- package/dist/shell/frame/devModel.js +17 -0
- package/dist/shell/frame/devModel.js.map +1 -0
- package/dist/shell/frame/diag.d.ts +7 -0
- package/dist/shell/frame/diag.js +39 -0
- package/dist/shell/frame/diag.js.map +1 -0
- package/dist/shell/frame/edges.css +159 -0
- package/dist/shell/frame/findBar.css +14 -0
- package/dist/shell/frame/frameMessages.d.ts +248 -0
- package/dist/shell/frame/frameMessages.js +252 -0
- package/dist/shell/frame/frameMessages.js.map +1 -0
- package/dist/shell/frame/hooks.d.ts +39 -0
- package/dist/shell/frame/hooks.js +97 -0
- package/dist/shell/frame/hooks.js.map +1 -0
- package/dist/shell/frame/i18n.d.ts +27 -0
- package/dist/shell/frame/i18n.js +66 -0
- package/dist/shell/frame/i18n.js.map +1 -0
- package/dist/shell/frame/iconPack.d.ts +11 -0
- package/dist/shell/frame/iconPack.js +45 -0
- package/dist/shell/frame/iconPack.js.map +1 -0
- package/dist/shell/frame/icons.d.ts +71 -0
- package/dist/shell/frame/icons.js +75 -0
- package/dist/shell/frame/icons.js.map +1 -0
- package/dist/shell/frame/index.d.ts +34 -0
- package/dist/shell/frame/index.js +43 -0
- package/dist/shell/frame/index.js.map +1 -0
- package/dist/shell/frame/language.d.ts +58 -0
- package/dist/shell/frame/language.js +165 -0
- package/dist/shell/frame/language.js.map +1 -0
- package/dist/shell/frame/mediaModel.d.ts +47 -0
- package/dist/shell/frame/mediaModel.js +67 -0
- package/dist/shell/frame/mediaModel.js.map +1 -0
- package/dist/shell/frame/menuMessages.en.json +96 -0
- package/dist/shell/frame/menuModel.d.ts +319 -0
- package/dist/shell/frame/menuModel.js +301 -0
- package/dist/shell/frame/menuModel.js.map +1 -0
- package/dist/shell/frame/nativeMenu.d.ts +5 -0
- package/dist/shell/frame/nativeMenu.js +178 -0
- package/dist/shell/frame/nativeMenu.js.map +1 -0
- package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
- package/dist/shell/frame/nativeMenuModel.js +186 -0
- package/dist/shell/frame/nativeMenuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.d.ts +139 -0
- package/dist/shell/frame/paletteModel.js +114 -0
- package/dist/shell/frame/paletteModel.js.map +1 -0
- package/dist/shell/frame/paneTheme.css +9 -0
- package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
- package/dist/shell/frame/pluginCommandStore.js +70 -0
- package/dist/shell/frame/pluginCommandStore.js.map +1 -0
- package/dist/shell/frame/reportModel.d.ts +52 -0
- package/dist/shell/frame/reportModel.js +105 -0
- package/dist/shell/frame/reportModel.js.map +1 -0
- package/dist/shell/frame/safeStore.d.ts +7 -0
- package/dist/shell/frame/safeStore.js +40 -0
- package/dist/shell/frame/safeStore.js.map +1 -0
- package/dist/shell/frame/searchInput.d.ts +2 -0
- package/dist/shell/frame/searchInput.js +11 -0
- package/dist/shell/frame/searchInput.js.map +1 -0
- package/dist/shell/frame/shell.css +634 -0
- package/dist/shell/frame/site.d.ts +206 -0
- package/dist/shell/frame/site.js +22 -0
- package/dist/shell/frame/site.js.map +1 -0
- package/dist/shell/frame/state.d.ts +101 -0
- package/dist/shell/frame/state.js +286 -0
- package/dist/shell/frame/state.js.map +1 -0
- package/dist/shell/frame/syncGate.d.ts +10 -0
- package/dist/shell/frame/syncGate.js +36 -0
- package/dist/shell/frame/syncGate.js.map +1 -0
- package/dist/shell/frame/syncHost.d.ts +35 -0
- package/dist/shell/frame/syncHost.js +85 -0
- package/dist/shell/frame/syncHost.js.map +1 -0
- package/dist/shell/frame/tabStill.d.ts +2 -0
- package/dist/shell/frame/tabStill.js +62 -0
- package/dist/shell/frame/tabStill.js.map +1 -0
- package/dist/shell/frame/themeScopes.d.ts +29 -0
- package/dist/shell/frame/themeScopes.js +61 -0
- package/dist/shell/frame/themeScopes.js.map +1 -0
- package/dist/shell/frame/themeStore.d.ts +99 -0
- package/dist/shell/frame/themeStore.js +267 -0
- package/dist/shell/frame/themeStore.js.map +1 -0
- package/dist/shell/frame/windowBinding.d.ts +2 -0
- package/dist/shell/frame/windowBinding.js +7 -0
- package/dist/shell/frame/windowBinding.js.map +1 -0
- package/dist/shell/frame/workspaceModel.d.ts +82 -0
- package/dist/shell/frame/workspaceModel.js +250 -0
- package/dist/shell/frame/workspaceModel.js.map +1 -0
- package/dist/shell/frame/workspaces.css +84 -0
- package/dist/shell/frame/workspaces.d.ts +22 -0
- package/dist/shell/frame/workspaces.js +125 -0
- package/dist/shell/frame/workspaces.js.map +1 -0
- package/dist/shell/frame/zoomStore.d.ts +10 -0
- package/dist/shell/frame/zoomStore.js +65 -0
- package/dist/shell/frame/zoomStore.js.map +1 -0
- package/dist/shell/installController.d.ts +137 -0
- package/dist/shell/installController.js +172 -0
- package/dist/shell/installController.js.map +1 -0
- package/dist/shell/installerMessages.d.ts +30 -0
- package/dist/shell/installerMessages.js +36 -0
- package/dist/shell/installerMessages.js.map +1 -0
- package/dist/shell/linkDrop.js.map +1 -1
- package/dist/shell/messages.d.ts +16 -0
- package/dist/shell/messages.js +27 -0
- package/dist/shell/messages.js.map +1 -0
- package/dist/shell/palette.js.map +1 -1
- package/dist/shell/paneModel.d.ts +22 -0
- package/dist/shell/paneModel.js +24 -1
- package/dist/shell/paneModel.js.map +1 -1
- package/dist/shell/panel/Appearance.d.ts +8 -0
- package/dist/shell/panel/Appearance.js +265 -0
- package/dist/shell/panel/Appearance.js.map +1 -0
- package/dist/shell/panel/BackupPanel.d.ts +5 -0
- package/dist/shell/panel/BackupPanel.js +198 -0
- package/dist/shell/panel/BackupPanel.js.map +1 -0
- package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
- package/dist/shell/panel/DeveloperPanel.js +51 -0
- package/dist/shell/panel/DeveloperPanel.js.map +1 -0
- package/dist/shell/panel/FeatureToggles.d.ts +13 -0
- package/dist/shell/panel/FeatureToggles.js +42 -0
- package/dist/shell/panel/FeatureToggles.js.map +1 -0
- package/dist/shell/panel/Home.d.ts +3 -0
- package/dist/shell/panel/Home.js +23 -0
- package/dist/shell/panel/Home.js.map +1 -0
- package/dist/shell/panel/IconPackInstall.d.ts +15 -0
- package/dist/shell/panel/IconPackInstall.js +41 -0
- package/dist/shell/panel/IconPackInstall.js.map +1 -0
- package/dist/shell/panel/LanguageSettings.d.ts +1 -0
- package/dist/shell/panel/LanguageSettings.js +54 -0
- package/dist/shell/panel/LanguageSettings.js.map +1 -0
- package/dist/shell/panel/MakePanel.d.ts +3 -0
- package/dist/shell/panel/MakePanel.js +237 -0
- package/dist/shell/panel/MakePanel.js.map +1 -0
- package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
- package/dist/shell/panel/MarkdownDoc.js +35 -0
- package/dist/shell/panel/MarkdownDoc.js.map +1 -0
- package/dist/shell/panel/Modes.d.ts +40 -0
- package/dist/shell/panel/Modes.js +270 -0
- package/dist/shell/panel/Modes.js.map +1 -0
- package/dist/shell/panel/NavIcon.d.ts +4 -0
- package/dist/shell/panel/NavIcon.js +6 -0
- package/dist/shell/panel/NavIcon.js.map +1 -0
- package/dist/shell/panel/Panel.d.ts +7 -0
- package/dist/shell/panel/Panel.js +139 -0
- package/dist/shell/panel/Panel.js.map +1 -0
- package/dist/shell/panel/PluginFeatures.d.ts +24 -0
- package/dist/shell/panel/PluginFeatures.js +165 -0
- package/dist/shell/panel/PluginFeatures.js.map +1 -0
- package/dist/shell/panel/Plugins.d.ts +33 -0
- package/dist/shell/panel/Plugins.js +112 -0
- package/dist/shell/panel/Plugins.js.map +1 -0
- package/dist/shell/panel/PowerSettings.d.ts +4 -0
- package/dist/shell/panel/PowerSettings.js +52 -0
- package/dist/shell/panel/PowerSettings.js.map +1 -0
- package/dist/shell/panel/Settings.d.ts +23 -0
- package/dist/shell/panel/Settings.js +117 -0
- package/dist/shell/panel/Settings.js.map +1 -0
- package/dist/shell/panel/ShareSetup.d.ts +8 -0
- package/dist/shell/panel/ShareSetup.js +130 -0
- package/dist/shell/panel/ShareSetup.js.map +1 -0
- package/dist/shell/panel/StoreSection.d.ts +7 -0
- package/dist/shell/panel/StoreSection.js +78 -0
- package/dist/shell/panel/StoreSection.js.map +1 -0
- package/dist/shell/panel/StringPacks.d.ts +20 -0
- package/dist/shell/panel/StringPacks.js +131 -0
- package/dist/shell/panel/StringPacks.js.map +1 -0
- package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
- package/dist/shell/panel/ThemeBuilder.js +205 -0
- package/dist/shell/panel/ThemeBuilder.js.map +1 -0
- package/dist/shell/panel/backup.css +34 -0
- package/dist/shell/panel/builder.css +240 -0
- package/dist/shell/panel/builderModel.d.ts +55 -0
- package/dist/shell/panel/builderModel.js +95 -0
- package/dist/shell/panel/builderModel.js.map +1 -0
- package/dist/shell/panel/compare.d.ts +10 -0
- package/dist/shell/panel/compare.js +51 -0
- package/dist/shell/panel/compare.js.map +1 -0
- package/dist/shell/panel/controls.d.ts +135 -0
- package/dist/shell/panel/controls.js +213 -0
- package/dist/shell/panel/controls.js.map +1 -0
- package/dist/shell/panel/featureModel.d.ts +69 -0
- package/dist/shell/panel/featureModel.js +79 -0
- package/dist/shell/panel/featureModel.js.map +1 -0
- package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
- package/dist/shell/panel/fonts/FontBrowser.js +189 -0
- package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
- package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
- package/dist/shell/panel/fonts/FontRoles.js +77 -0
- package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
- package/dist/shell/panel/fonts/catalog.d.ts +37 -0
- package/dist/shell/panel/fonts/catalog.js +68 -0
- package/dist/shell/panel/fonts/catalog.js.map +1 -0
- package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
- package/dist/shell/panel/index.d.ts +20 -0
- package/dist/shell/panel/index.js +34 -0
- package/dist/shell/panel/index.js.map +1 -0
- package/dist/shell/panel/languageModel.d.ts +5 -0
- package/dist/shell/panel/languageModel.js +7 -0
- package/dist/shell/panel/languageModel.js.map +1 -0
- package/dist/shell/panel/make.css +73 -0
- package/dist/shell/panel/makeModel.d.ts +32 -0
- package/dist/shell/panel/makeModel.js +84 -0
- package/dist/shell/panel/makeModel.js.map +1 -0
- package/dist/shell/panel/markdown.d.ts +46 -0
- package/dist/shell/panel/markdown.js +100 -0
- package/dist/shell/panel/markdown.js.map +1 -0
- package/dist/shell/panel/nav.d.ts +12 -0
- package/dist/shell/panel/nav.js +50 -0
- package/dist/shell/panel/nav.js.map +1 -0
- package/dist/shell/panel/panel.css +401 -0
- package/dist/shell/panel/panelMessages.d.ts +803 -0
- package/dist/shell/panel/panelMessages.js +779 -0
- package/dist/shell/panel/panelMessages.js.map +1 -0
- package/dist/shell/panel/plugins.css +59 -0
- package/dist/shell/panel/setupModel.d.ts +87 -0
- package/dist/shell/panel/setupModel.js +152 -0
- package/dist/shell/panel/setupModel.js.map +1 -0
- package/dist/shell/panel/site.d.ts +259 -0
- package/dist/shell/panel/site.js +25 -0
- package/dist/shell/panel/site.js.map +1 -0
- package/dist/shell/panel/strings.css +7 -0
- package/dist/shell/panel/themes.d.ts +10 -0
- package/dist/shell/panel/themes.js +8 -0
- package/dist/shell/panel/themes.js.map +1 -0
- package/dist/shell/profileModel.d.ts +3 -2
- package/dist/shell/profileModel.js +7 -5
- package/dist/shell/profileModel.js.map +1 -1
- package/dist/shell/store.css +250 -0
- package/dist/shell/storeMessages.d.ts +193 -0
- package/dist/shell/storeMessages.js +196 -0
- package/dist/shell/storeMessages.js.map +1 -0
- package/dist/shell/storeModel.d.ts +205 -0
- package/dist/shell/storeModel.js +146 -0
- package/dist/shell/storeModel.js.map +1 -0
- package/dist/shell/stringPacksModel.d.ts +44 -0
- package/dist/shell/stringPacksModel.js +99 -0
- package/dist/shell/stringPacksModel.js.map +1 -0
- package/dist/shell/updateModel.d.ts +2 -1
- package/dist/shell/updateModel.js +5 -4
- package/dist/shell/updateModel.js.map +1 -1
- package/dist/shell/windowControls.d.ts +4 -0
- package/dist/shell/windowControls.js +5 -0
- package/dist/shell/windowControls.js.map +1 -1
- package/i18n-config.schema.json +29 -0
- package/i18n-meta.schema.json +26 -0
- package/i18n.schema.json +42 -0
- package/icons.schema.json +655 -0
- package/languages.schema.json +23 -0
- package/locales/ar-x-fm.json +11 -0
- package/locales/ar.json +1388 -0
- package/locales/bg.json +1387 -0
- package/locales/bn.json +1387 -0
- package/locales/ca.json +1387 -0
- package/locales/cs.json +1387 -0
- package/locales/da.json +1387 -0
- package/locales/de.json +1387 -0
- package/locales/el.json +1387 -0
- package/locales/en-GB.json +27 -0
- package/locales/en.json +1386 -0
- package/locales/en.meta.json +51 -0
- package/locales/es.json +1387 -0
- package/locales/eu.json +1387 -0
- package/locales/fa.json +1388 -0
- package/locales/fi.json +1387 -0
- package/locales/fil.json +1387 -0
- package/locales/fr.json +1387 -0
- package/locales/ga.json +1387 -0
- package/locales/gl.json +1387 -0
- package/locales/gu.json +1387 -0
- package/locales/ha.json +1387 -0
- package/locales/he.json +1388 -0
- package/locales/hi.json +1387 -0
- package/locales/hr.json +1387 -0
- package/locales/hu.json +1387 -0
- package/locales/id.json +1387 -0
- package/locales/ig.json +1387 -0
- package/locales/it.json +1387 -0
- package/locales/ja.json +1387 -0
- package/locales/kn.json +1387 -0
- package/locales/ko.json +1387 -0
- package/locales/mr.json +1387 -0
- package/locales/ms.json +1387 -0
- package/locales/nb.json +1387 -0
- package/locales/nl.json +1387 -0
- package/locales/pl.json +1387 -0
- package/locales/pt.json +1387 -0
- package/locales/ro.json +1387 -0
- package/locales/ru.json +1387 -0
- package/locales/sk.json +1387 -0
- package/locales/sr.json +1387 -0
- package/locales/sv.json +1387 -0
- package/locales/ta.json +1387 -0
- package/locales/th.json +1387 -0
- package/locales/tr.json +1387 -0
- package/locales/uk.json +1387 -0
- package/locales/ur.json +1388 -0
- package/locales/vi.json +1387 -0
- package/locales/yo.json +1387 -0
- package/locales/zh-Hans.json +1387 -0
- package/locales/zh-Hant.json +1387 -0
- package/package.json +138 -5
- package/store-catalog.schema.json +78 -0
- package/theme.schema.json +4668 -0
- package/windows/installer.nsi +946 -0
- package/windows/languages/Basque.nsh +28 -0
- package/windows/languages/Catalan.nsh +28 -0
- package/windows/languages/Croatian.nsh +28 -0
- package/windows/languages/Czech.nsh +28 -0
- package/windows/languages/Danish.nsh +28 -0
- package/windows/languages/Finnish.nsh +28 -0
- package/windows/languages/Galician.nsh +28 -0
- package/windows/languages/Greek.nsh +28 -0
- package/windows/languages/Hungarian.nsh +28 -0
- package/windows/languages/Indonesian.nsh +28 -0
- package/windows/languages/Irish.nsh +28 -0
- package/windows/languages/Malay.nsh +28 -0
- package/windows/languages/Polish.nsh +28 -0
- package/windows/languages/Romanian.nsh +28 -0
- package/windows/languages/Serbian.nsh +28 -0
- package/windows/languages/Slovak.nsh +28 -0
- package/windows/languages/Thai.nsh +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../runtime/src/i18n.ts"],"names":[],"mappings":"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","sourcesContent":["// wayBetter i18n: a small, dependency-free message runtime for the shell, the panel, the page\n// runtime and plugins (published as `waybetter/i18n`).\n//\n// Messages are plain strings in an ICU MessageFormat subset (\"ICU-lite\"):\n// Hello, {name}! placeholder (inserted as text, never HTML)\n// {count, plural, =0 {No drafts} one {# draft} other {# drafts}} plural (Intl.PluralRules)\n// {n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} ordinal\n// {kind, select, post {Post} reply {Reply} other {Item}} select\n// {n, number} {n, number, percent} {d, date, short} {d, time} Intl number/date formats\n// '{literal}' '#' and '' (an apostrophe) ICU quoting\n// An apostrophe quotes only when it comes right before { } # or another apostrophe, so ordinary\n// English (\"don't\", \"the developer's\") needs no escaping.\n// `#` inside a plural is the (locale-formatted) number. Numbers, dates, relative times and lists\n// go through `Intl` in the active locale.\n//\n// Catalogs are data only: one JSON file per locale (`locales/<bcp47>.json`), English is the\n// source. Lookups fall back along a chain (pt-BR -> pt -> en). Missing keys are reported once per\n// key and locale through `onMissing` (development), never thrown.\n\nexport type Messages = Readonly<Record<string, string>>;\nexport type Dir = 'ltr' | 'rtl';\nexport type Params = Readonly<Record<string, unknown>>;\n\n/** A catalog file (`locales/<bcp47>.json`). See `i18n.schema.json` in the `waybetter` package. */\nexport type Catalog = {\n $schema?: string;\n /** BCP 47 tag of this file (`tr`, `pt`, `zh-Hant`, `ar-x-fm`). */\n locale: string;\n /** The language's own name (\"Türkçe\"). */\n name: string;\n /** Its English name (\"Turkish\"). */\n englishName: string;\n dir?: Dir;\n /** `source` (English), `machine` (machine-drafted, needs a human), `reviewed` (checked by people). */\n status: 'source' | 'machine' | 'reviewed';\n reviewers?: string[];\n /** Falls back to this locale before English (e.g. `ar-x-fm` -> `ar`). */\n fallback?: string;\n messages: Record<string, string>;\n};\n\n// -------------------------------------------------------------------------------------------\n// Locales\n\n/** Scripts written right to left, by primary language subtag. */\nconst RTL = new Set(['ar', 'arc', 'ckb', 'dv', 'fa', 'he', 'iw', 'ks', 'ku-arab', 'ps', 'sd', 'syr', 'ug', 'ur', 'yi']);\n\n/**\n * Site and legacy codes mapped to BCP 47: X's own settings use `msa`, `zh-cn`, `zh-tw`, `no` and\n * `en-gb`; old platforms say `iw`, `in`, `tl`.\n */\nconst ALIASES: Record<string, string> = {\n msa: 'ms',\n 'zh-cn': 'zh-Hans',\n 'zh-sg': 'zh-Hans',\n zh: 'zh-Hans',\n 'zh-tw': 'zh-Hant',\n 'zh-hk': 'zh-Hant',\n 'zh-mo': 'zh-Hant',\n no: 'nb',\n nn: 'nb',\n iw: 'he',\n in: 'id',\n tl: 'fil',\n ji: 'yi',\n};\n\nconst TAG = /^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8}){0,4}$/;\n\n/**\n * A tag in canonical BCP 47 form (`en_gb` -> `en-GB`, `zh-cn` -> `zh-Hans`, `msa` -> `ms`), or null\n * when it is not a language tag at all. Private-use subtags survive (`ar-x-fm`).\n */\nexport function normalizeLocale(input: unknown): string | null {\n if (typeof input !== 'string') return null;\n const raw = input.trim().replace(/_/g, '-');\n if (!raw || raw.length > 35 || !TAG.test(raw)) return null;\n const lower = raw.toLowerCase();\n if (ALIASES[lower]) return ALIASES[lower];\n // `zh-Hans-CN` / `zh-Hant-TW`: the script decides; the region is noise for our catalogs.\n const parts = lower.split('-');\n if (parts[0] === 'zh') {\n if (parts.includes('hant') || ['tw', 'hk', 'mo'].includes(parts[1] ?? '')) return 'zh-Hant';\n return 'zh-Hans';\n }\n const privateUse = lower.indexOf('-x-');\n if (privateUse > 0) {\n const base = normalizeLocale(raw.slice(0, privateUse));\n return base ? `${base}-x-${lower.slice(privateUse + 3)}` : null;\n }\n const alias = ALIASES[parts[0]!];\n const head = alias && !alias.includes('-') ? [alias, ...parts.slice(1)].join('-') : raw;\n try {\n return Intl.getCanonicalLocales(head)[0] ?? null;\n } catch {\n return null;\n }\n}\n\n/** The primary language subtag (`pt-BR` -> `pt`). */\nexport function languageOf(locale: string): string {\n return locale.split('-')[0]!.toLowerCase();\n}\n\n/** Writing direction of a locale. */\nexport function direction(locale: string): Dir {\n const lower = locale.toLowerCase();\n if (lower.startsWith('ku-arab')) return 'rtl';\n return RTL.has(languageOf(lower)) ? 'rtl' : 'ltr';\n}\n\n/**\n * Where lookups go, most specific first, ending with the source locale: `pt-BR` -> [pt-BR, pt, en].\n * `fallbacks` maps a locale to the one it inherits from (a catalog's `fallback`, e.g. ar-x-fm -> ar).\n * Script variants never fall back to another script: `zh-Hant` does not use `zh-Hans`.\n */\nexport function fallbackChain(locale: string, source = 'en', fallbacks: Readonly<Record<string, string>> = {}): string[] {\n const out: string[] = [];\n const push = (tag: string | null | undefined) => {\n if (tag && !out.includes(tag)) out.push(tag);\n };\n let current: string | null = normalizeLocale(locale);\n for (let guard = 0; current && guard < 8; guard += 1) {\n push(current);\n const explicit = fallbacks[current];\n if (explicit) {\n current = normalizeLocale(explicit);\n continue;\n }\n const privateUse = current.indexOf('-x-');\n if (privateUse > 0) {\n current = current.slice(0, privateUse);\n continue;\n }\n const parts = current.split('-');\n if (parts.length <= 1) break;\n // zh-Hans / zh-Hant, sr-Latn: a script subtag is its own catalog.\n if (parts.length === 2 && /^[A-Z][a-z]{3}$/.test(parts[1]!)) break;\n current = parts.slice(0, -1).join('-');\n }\n push(source);\n return out;\n}\n\n/**\n * Picks the best available locale for a list of preferences (most wanted first), by fallback chain:\n * `['pt-PT', 'en']` with `pt` available -> `pt`. Returns `fallback` when nothing matches.\n */\nexport function matchLocale(preferred: readonly unknown[], available: readonly string[], fallback = 'en'): string {\n const set = new Set(available);\n for (const wanted of preferred) {\n const tag = normalizeLocale(wanted);\n if (!tag) continue;\n for (const candidate of fallbackChain(tag, '')) {\n if (candidate && set.has(candidate)) return candidate;\n }\n }\n return fallback;\n}\n\n// -------------------------------------------------------------------------------------------\n// Messages (ICU-lite)\n\ntype Part =\n | string\n | { kind: 'arg'; name: string; format?: 'number' | 'date' | 'time'; style?: string }\n | { kind: 'pound' }\n | { kind: 'plural'; name: string; ordinal: boolean; offset: number; options: Record<string, Part[]> }\n | { kind: 'select'; name: string; options: Record<string, Part[]> };\n\nexport class MessageSyntaxError extends Error {\n readonly source: string;\n readonly index: number;\n constructor(message: string, source: string, index: number) {\n super(`${message} at ${index} in ${JSON.stringify(source.length > 80 ? `${source.slice(0, 80)}…` : source)}`);\n this.name = 'MessageSyntaxError';\n this.source = source;\n this.index = index;\n }\n}\n\nconst NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/** Parses a message into parts; throws `MessageSyntaxError` on malformed ICU. */\nexport function parseMessage(source: string): Part[] {\n let i = 0;\n\n const parseParts = (inPlural: boolean, depth: number): Part[] => {\n const parts: Part[] = [];\n let text = '';\n const flush = () => {\n if (text) parts.push(text);\n text = '';\n };\n while (i < source.length) {\n const ch = source[i]!;\n if (ch === \"'\") {\n const next = source[i + 1];\n if (next === \"'\") { text += \"'\"; i += 2; continue; }\n // An apostrophe only quotes before { } # (or another apostrophe); \"don't\" stays literal.\n if (next === '{' || next === '}' || next === '#') {\n // Quoted literal up to the next lone apostrophe.\n i += 1;\n while (i < source.length) {\n if (source[i] === \"'\") {\n if (source[i + 1] === \"'\") { text += \"'\"; i += 2; continue; }\n i += 1;\n break;\n }\n text += source[i];\n i += 1;\n }\n continue;\n }\n text += ch;\n i += 1;\n continue;\n }\n if (ch === '}') {\n if (depth > 0) break;\n throw new MessageSyntaxError('unexpected }', source, i);\n }\n if (ch === '#' && inPlural) {\n flush();\n parts.push({ kind: 'pound' });\n i += 1;\n continue;\n }\n if (ch === '{') {\n flush();\n parts.push(parseArgument(depth));\n continue;\n }\n text += ch;\n i += 1;\n }\n flush();\n return parts;\n };\n\n const skipSpace = () => { while (i < source.length && /\\s/.test(source[i]!)) i += 1; };\n const readWord = () => {\n const start = i;\n while (i < source.length && /[^\\s,{}]/.test(source[i]!)) i += 1;\n return source.slice(start, i);\n };\n const expect = (ch: string) => {\n if (source[i] !== ch) throw new MessageSyntaxError(`expected ${ch}`, source, i);\n i += 1;\n };\n\n const parseOptions = (inPlural: boolean, depth: number): Record<string, Part[]> => {\n const options: Record<string, Part[]> = {};\n for (;;) {\n skipSpace();\n if (source[i] === '}') break;\n const selector = readWord();\n if (!selector) throw new MessageSyntaxError('expected a selector', source, i);\n skipSpace();\n expect('{');\n options[selector] = parseParts(inPlural, depth + 1);\n expect('}');\n }\n if (!('other' in options)) throw new MessageSyntaxError('a plural or select needs an \"other\" option', source, i);\n return options;\n };\n\n const parseArgument = (depth: number): Part => {\n expect('{');\n skipSpace();\n const name = readWord();\n if (!NAME.test(name)) throw new MessageSyntaxError(`invalid placeholder name ${JSON.stringify(name)}`, source, i);\n skipSpace();\n if (source[i] === '}') { i += 1; return { kind: 'arg', name }; }\n expect(',');\n skipSpace();\n const type = readWord();\n skipSpace();\n if (type === 'plural' || type === 'selectordinal') {\n expect(',');\n skipSpace();\n let offset = 0;\n const offsetMatch = /^offset:(\\d+)/.exec(source.slice(i));\n if (offsetMatch) { offset = Number(offsetMatch[1]); i += offsetMatch[0].length; }\n const options = parseOptions(true, depth);\n expect('}');\n return { kind: 'plural', name, ordinal: type === 'selectordinal', offset, options };\n }\n if (type === 'select') {\n expect(',');\n const options = parseOptions(false, depth);\n expect('}');\n return { kind: 'select', name, options };\n }\n if (type === 'number' || type === 'date' || type === 'time') {\n let style: string | undefined;\n if (source[i] === ',') {\n i += 1;\n skipSpace();\n style = readWord();\n skipSpace();\n }\n expect('}');\n return { kind: 'arg', name, format: type, style };\n }\n throw new MessageSyntaxError(`unknown argument type ${JSON.stringify(type)}`, source, i);\n };\n\n const parts = parseParts(false, 0);\n if (i < source.length) throw new MessageSyntaxError('unexpected }', source, i);\n return parts;\n}\n\n/** Placeholder names a message uses (plural/select arguments included), sorted. */\nexport function placeholders(source: string): string[] {\n const names = new Set<string>();\n const walk = (parts: Part[]) => {\n for (const part of parts) {\n if (typeof part === 'string' || part.kind === 'pound') continue;\n names.add(part.name);\n if (part.kind === 'plural' || part.kind === 'select') for (const option of Object.values(part.options)) walk(option);\n }\n };\n walk(parseMessage(source));\n return [...names].sort();\n}\n\n/** Plural/select structure of a message: `{ count: { kind: 'plural', options: ['one', 'other'] } }`. */\nexport function argumentShapes(source: string): Record<string, { kind: 'plural' | 'selectordinal' | 'select'; options: string[] }> {\n const out: Record<string, { kind: 'plural' | 'selectordinal' | 'select'; options: string[] }> = {};\n const walk = (parts: Part[]) => {\n for (const part of parts) {\n if (typeof part === 'string' || part.kind === 'pound' || part.kind === 'arg') continue;\n const kind = part.kind === 'select' ? 'select' : part.ordinal ? 'selectordinal' : 'plural';\n const entry = (out[part.name] ??= { kind, options: [] });\n for (const [selector, option] of Object.entries(part.options)) {\n if (!entry.options.includes(selector)) entry.options.push(selector);\n walk(option);\n }\n }\n };\n walk(parseMessage(source));\n return out;\n}\n\nconst formatterCache = new Map<string, Intl.NumberFormat | Intl.DateTimeFormat | Intl.PluralRules>();\nfunction cached<T extends Intl.NumberFormat | Intl.DateTimeFormat | Intl.PluralRules>(key: string, make: () => T): T {\n let value = formatterCache.get(key) as T | undefined;\n if (!value) {\n value = make();\n if (formatterCache.size > 256) formatterCache.clear();\n formatterCache.set(key, value);\n }\n return value;\n}\n\nfunction safeLocale(locale: string): string {\n // Private-use tags (ar-x-fm) are valid BCP 47 but Intl may not know them: format as the base.\n const base = locale.split('-x-')[0]!;\n try {\n return Intl.getCanonicalLocales(base)[0] ?? 'en';\n } catch {\n return 'en';\n }\n}\n\nexport function numberFormat(locale: string, options?: Intl.NumberFormatOptions): Intl.NumberFormat {\n const tag = safeLocale(locale);\n return cached(`n|${tag}|${JSON.stringify(options ?? {})}`, () => new Intl.NumberFormat(tag, options));\n}\n\nexport function dateFormat(locale: string, options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const tag = safeLocale(locale);\n return cached(`d|${tag}|${JSON.stringify(options ?? {})}`, () => new Intl.DateTimeFormat(tag, options));\n}\n\nexport function pluralRules(locale: string, ordinal = false): Intl.PluralRules {\n const tag = safeLocale(locale);\n return cached(`p|${tag}|${ordinal}`, () => new Intl.PluralRules(tag, { type: ordinal ? 'ordinal' : 'cardinal' }));\n}\n\n/** CLDR plural categories a locale uses (`tr`: one, other; `ar`: zero, one, two, few, many, other). */\nexport function pluralCategories(locale: string, ordinal = false): string[] {\n return [...pluralRules(locale, ordinal).resolvedOptions().pluralCategories];\n}\n\nconst NUMBER_STYLES: Record<string, Intl.NumberFormatOptions> = {\n integer: { maximumFractionDigits: 0 },\n percent: { style: 'percent' },\n compact: { notation: 'compact' },\n};\nconst DATE_STYLES: Record<string, Intl.DateTimeFormatOptions> = {\n short: { dateStyle: 'short' },\n medium: { dateStyle: 'medium' },\n long: { dateStyle: 'long' },\n full: { dateStyle: 'full' },\n};\nconst TIME_STYLES: Record<string, Intl.DateTimeFormatOptions> = {\n short: { timeStyle: 'short' },\n medium: { timeStyle: 'medium' },\n long: { timeStyle: 'long' },\n full: { timeStyle: 'full' },\n};\n\nfunction toDate(value: unknown): Date | null {\n const date = value instanceof Date ? value : typeof value === 'number' || typeof value === 'string' ? new Date(value) : null;\n return date && Number.isFinite(date.getTime()) ? date : null;\n}\n\nfunction render(parts: Part[], params: Params, locale: string, pound: number | null, onMissingParam?: (name: string) => void): string {\n let out = '';\n for (const part of parts) {\n if (typeof part === 'string') { out += part; continue; }\n if (part.kind === 'pound') {\n out += pound === null ? '#' : numberFormat(locale).format(pound);\n continue;\n }\n const has = Object.prototype.hasOwnProperty.call(params, part.name);\n const value = has ? params[part.name] : undefined;\n if (!has || value === undefined || value === null) {\n onMissingParam?.(part.name);\n out += `{${part.name}}`;\n continue;\n }\n if (part.kind === 'arg') {\n if (part.format === 'number' && typeof value === 'number') {\n out += numberFormat(locale, NUMBER_STYLES[part.style ?? ''] ?? undefined).format(value);\n } else if ((part.format === 'date' || part.format === 'time') && toDate(value)) {\n const styles = part.format === 'date' ? DATE_STYLES : TIME_STYLES;\n out += dateFormat(locale, styles[part.style ?? 'medium'] ?? styles.medium).format(toDate(value)!);\n } else if (typeof value === 'number') {\n out += numberFormat(locale).format(value);\n } else {\n out += String(value);\n }\n continue;\n }\n if (part.kind === 'select') {\n const key = String(value);\n out += render(part.options[key] ?? part.options.other!, params, locale, pound, onMissingParam);\n continue;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) { out += String(value); continue; }\n const exact = part.options[`=${n}`];\n const shifted = n - part.offset;\n const option = exact ?? part.options[pluralRules(locale, part.ordinal).select(shifted)] ?? part.options.other!;\n out += render(option, params, locale, shifted, onMissingParam);\n }\n return out;\n}\n\nconst compiled = new Map<string, Part[] | Error>();\n\n/**\n * Formats one message. Placeholder values are inserted as plain text; a malformed message renders\n * as its raw text (and `onError` hears about it) rather than throwing in the UI.\n */\nexport function formatMessage(\n source: string,\n params: Params = {},\n locale = 'en',\n hooks: { onError?: (error: Error) => void; onMissingParam?: (name: string) => void } = {},\n): string {\n if (!source.includes('{') && !source.includes(\"'\")) return source;\n let parts = compiled.get(source);\n if (!parts) {\n try {\n parts = parseMessage(source);\n } catch (error) {\n parts = error instanceof Error ? error : new Error(String(error));\n }\n if (compiled.size > 4096) compiled.clear();\n compiled.set(source, parts);\n }\n if (parts instanceof Error) {\n hooks.onError?.(parts);\n return source;\n }\n return render(parts, params, locale, null, hooks.onMissingParam);\n}\n\n// -------------------------------------------------------------------------------------------\n// Runtime\n\nexport type I18nOptions = {\n /** The source locale; its messages are complete (default `en`). */\n sourceLocale?: string;\n /** Source messages (English). Always loaded. */\n source: Messages;\n /** Locales that have catalogs (the source included). */\n available: readonly string[];\n /** Per-locale inheritance before the source (a catalog's `fallback`). */\n fallbacks?: Readonly<Record<string, string>>;\n /** Loads one locale's messages (lazy import of `locales/<tag>.json`), or null when there are none. */\n load?: (locale: string) => Promise<Messages | null | undefined> | Messages | null | undefined;\n /** Initial locale (default: the source). */\n locale?: string;\n /** A key the chain does not have, or a message that failed to parse. Once per key and locale. */\n onMissing?: (key: string, locale: string, reason: 'key' | 'syntax' | 'param') => void;\n};\n\nexport type I18n<K extends string = string> = {\n /** Active locale (a catalog tag, e.g. `pt`, `zh-Hant`). */\n readonly locale: string;\n readonly dir: Dir;\n /** Lookup order for the active locale, ending with the source. */\n readonly chain: readonly string[];\n readonly available: readonly string[];\n /** The message for `key` in the active locale, formatted with `params`. Unknown keys return the key. */\n t(key: K, params?: Params): string;\n /** Like `t` for keys built at runtime; returns `fallback` (default the key) when missing. */\n tx(key: string, params?: Params, fallback?: string): string;\n has(key: string): boolean;\n /** Switches locale (loading its catalog and its chain's). Resolves with the effective locale. */\n setLocale(locale: string): Promise<string>;\n /** Adds messages for a locale (framework catalogs, plugin strings); later adds win. */\n add(locale: string, messages: Messages): void;\n onChange(listener: (locale: string) => void): () => void;\n /** Resolved messages of the active locale for keys starting with one of `prefixes` (the chain merged). */\n resolved(prefixes?: readonly string[]): Record<string, string>;\n number(value: number, options?: Intl.NumberFormatOptions): string;\n date(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;\n /** \"3 minutes ago\", \"in 2 days\" (Intl.RelativeTimeFormat, numeric: auto). */\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n /** \"a, b and c\" (Intl.ListFormat). */\n list(items: readonly string[], options?: { type?: 'conjunction' | 'disjunction' | 'unit'; style?: 'long' | 'short' | 'narrow' }): string;\n plural(count: number, ordinal?: boolean): Intl.LDMLPluralRule;\n};\n\nexport function createI18n<K extends string = string>(options: I18nOptions): I18n<K> {\n const source = options.sourceLocale ?? 'en';\n const available = Object.freeze([...new Set([source, ...options.available])]);\n const tables = new Map<string, Record<string, string>>();\n tables.set(source, { ...options.source });\n const loaded = new Set<string>([source]);\n const listeners = new Set<(locale: string) => void>();\n const reported = new Set<string>();\n let locale = source;\n let chain: string[] = [source];\n\n const report = (key: string, where: string, reason: 'key' | 'syntax' | 'param') => {\n const id = `${reason}|${where}|${key}`;\n if (reported.has(id) || !options.onMissing) return;\n if (reported.size > 2048) reported.clear();\n reported.add(id);\n try { options.onMissing(key, where, reason); } catch { /* a reporter must not break the UI */ }\n };\n\n const chainFor = (tag: string) => fallbackChain(tag, source, options.fallbacks).filter(entry => available.includes(entry));\n\n const lookup = (key: string): string | undefined => {\n for (const entry of chain) {\n const value = tables.get(entry)?.[key];\n if (typeof value === 'string') {\n if (entry !== locale && locale !== source && entry === source) report(key, locale, 'key');\n return value;\n }\n }\n return undefined;\n };\n\n const format = (key: string, message: string, params?: Params) =>\n formatMessage(message, params ?? {}, locale, {\n onError: () => report(key, locale, 'syntax'),\n onMissingParam: () => report(key, locale, 'param'),\n });\n\n const i18n: I18n<K> = {\n get locale() { return locale; },\n get dir() { return direction(locale); },\n get chain() { return chain; },\n available,\n t(key, params) {\n const message = lookup(key);\n if (message === undefined) {\n report(key, locale, 'key');\n return key;\n }\n return format(key, message, params);\n },\n tx(key, params, fallback) {\n const message = lookup(key);\n if (message === undefined) return fallback ?? key;\n return format(key, message, params);\n },\n has: key => lookup(key) !== undefined,\n async setLocale(requested) {\n const target = matchLocale([requested], available, source);\n const nextChain = chainFor(target);\n if (options.load) {\n await Promise.all(nextChain.filter(tag => !loaded.has(tag)).map(async tag => {\n try {\n const messages = await options.load!(tag);\n if (messages) tables.set(tag, { ...messages, ...tables.get(tag) });\n } catch (error) {\n report(`<catalog ${tag}: ${error instanceof Error ? error.message : String(error)}>`, tag, 'key');\n }\n loaded.add(tag);\n }));\n }\n const changed = target !== locale;\n locale = target;\n chain = nextChain;\n if (changed) for (const listener of [...listeners]) {\n try { listener(locale); } catch { /* listeners must not break each other */ }\n }\n return locale;\n },\n add(tag, messages) {\n const key = normalizeLocale(tag) ?? tag;\n tables.set(key, { ...tables.get(key), ...messages });\n },\n onChange(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n resolved(prefixes) {\n const out: Record<string, string> = {};\n for (const entry of [...chain].reverse()) {\n for (const [key, value] of Object.entries(tables.get(entry) ?? {})) {\n if (!prefixes || prefixes.some(prefix => key.startsWith(prefix))) out[key] = value;\n }\n }\n return out;\n },\n number: (value, opts) => numberFormat(locale, opts).format(value),\n date: (value, opts) => {\n const date = toDate(value);\n return date ? dateFormat(locale, opts).format(date) : '';\n },\n relative: (value, unit, opts) => {\n try {\n return new Intl.RelativeTimeFormat(safeLocale(locale), { numeric: 'auto', ...opts }).format(value, unit);\n } catch {\n return `${value} ${unit}`;\n }\n },\n list: (items, opts) => {\n try {\n return new Intl.ListFormat(safeLocale(locale), { type: opts?.type ?? 'conjunction', style: opts?.style ?? 'long' }).format(items);\n } catch {\n return items.join(', ');\n }\n },\n plural: (count, ordinal = false) => pluralRules(locale, ordinal).select(count),\n };\n return i18n;\n}\n\n/** Picks a relative-time unit for a timestamp: \"just now\" granularity down to seconds. */\nexport function relativeParts(then: number, now = Date.now()): { value: number; unit: Intl.RelativeTimeFormatUnit } {\n const seconds = Math.round((then - now) / 1000);\n const abs = Math.abs(seconds);\n if (abs < 60) return { value: seconds, unit: 'second' };\n if (abs < 3600) return { value: Math.round(seconds / 60), unit: 'minute' };\n if (abs < 86400) return { value: Math.round(seconds / 3600), unit: 'hour' };\n if (abs < 86400 * 30) return { value: Math.round(seconds / 86400), unit: 'day' };\n if (abs < 86400 * 365) return { value: Math.round(seconds / (86400 * 30)), unit: 'month' };\n return { value: Math.round(seconds / (86400 * 365)), unit: 'year' };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Catalog } from './i18n.ts';
|
|
2
|
+
export type CatalogIssue = {
|
|
3
|
+
level: 'error' | 'warning';
|
|
4
|
+
locale: string;
|
|
5
|
+
key?: string;
|
|
6
|
+
message: string;
|
|
7
|
+
};
|
|
8
|
+
/** `allowEmpty`: a sentence fragment some languages leave empty (word order moves it elsewhere). */
|
|
9
|
+
export type MetaEntry = {
|
|
10
|
+
description?: string;
|
|
11
|
+
maxLength?: number;
|
|
12
|
+
context?: string;
|
|
13
|
+
allowEmpty?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type Meta = Record<string, MetaEntry | string>;
|
|
16
|
+
/** Checks the file envelope (locale, names, status, reviewers, messages map). */
|
|
17
|
+
export declare function checkShape(input: unknown, file?: string): CatalogIssue[];
|
|
18
|
+
/** Safety rules every message obeys, source included. */
|
|
19
|
+
export declare function checkMessageSafety(locale: string, key: string, value: string): CatalogIssue[];
|
|
20
|
+
/**
|
|
21
|
+
* Checks one translated catalog against the source messages: extra keys, placeholder parity,
|
|
22
|
+
* plural categories, safety, untranslated and overlong messages.
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkCatalog(catalog: Catalog, source: Record<string, string>, meta?: Meta): CatalogIssue[];
|
|
25
|
+
/** Checks the source catalog itself: safety, syntax, and that every key is described in the meta file. */
|
|
26
|
+
export declare function checkSource(catalog: Catalog, meta?: Meta): CatalogIssue[];
|
|
27
|
+
/** Share of source keys a catalog translates (0..1), following its `fallback` catalog when given. */
|
|
28
|
+
export declare function coverage(catalog: Catalog, source: Record<string, string>, fallback?: Catalog): number;
|
|
29
|
+
/** TypeScript source for a key union and per-key parameter types, generated from source messages. */
|
|
30
|
+
export declare function generateKeyTypes(source: Record<string, string>, typeName?: string): string;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Catalog checks for translators and CI (published as `waybetter/i18n/check`): every rule a
|
|
2
|
+
// contributed `locales/<bcp47>.json` must pass before review. Pure functions over parsed JSON, so
|
|
3
|
+
// an app's `scripts/i18n-check.mjs`, its tests and editors can share them.
|
|
4
|
+
//
|
|
5
|
+
// Errors fail the check: bad file shape, keys the source does not have, malformed ICU, placeholder
|
|
6
|
+
// names English does not have, a missing placeholder (except an argument English only uses to choose
|
|
7
|
+
// a plural form, which languages without that inflection may drop), select arguments missing an
|
|
8
|
+
// English option, markup or control characters (catalogs are data, never HTML), empty translations
|
|
9
|
+
// (unless the meta file allows it for a sentence fragment). Warnings do not fail: plural forms the
|
|
10
|
+
// language uses but the translation leaves to `other`, untranslated keys (they fall back to
|
|
11
|
+
// English) and translations much longer than the source.
|
|
12
|
+
import { argumentShapes, direction, normalizeLocale, parseMessage, placeholders, pluralCategories } from "./i18n.js";
|
|
13
|
+
const KEY = /^[a-z][A-Za-z0-9]*(?:[._-][A-Za-z0-9]+)*$/;
|
|
14
|
+
/** Markup or script-ish content: tags, entities that decode to markup, `javascript:` and friends. */
|
|
15
|
+
const MARKUP = /<\s*\/?\s*[a-z!?]|&(?:lt|gt|#0*60|#x0*3c);|javascript:|data:text\/html/i;
|
|
16
|
+
const CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f--]/;
|
|
17
|
+
const STATUSES = new Set(['source', 'machine', 'reviewed']);
|
|
18
|
+
/** A long translation is fine up to this factor of the source length (plus some slack). */
|
|
19
|
+
const LENGTH_FACTOR = 1.8;
|
|
20
|
+
const LENGTH_SLACK = 16;
|
|
21
|
+
/**
|
|
22
|
+
* Whether `name` puts text in the message: a plain `{name}` / `{name, number}` somewhere, or a `#`
|
|
23
|
+
* (the number itself) in one of its own plural branches. A plural argument without either only
|
|
24
|
+
* chooses a form.
|
|
25
|
+
*/
|
|
26
|
+
function usedAsText(source, name) {
|
|
27
|
+
const pound = (parts) => parts.some(part => typeof part !== 'string' && (part.kind === 'pound'
|
|
28
|
+
|| (part.kind === 'select' && Object.values(part.options).some(pound))));
|
|
29
|
+
const walk = (parts) => parts.some(part => {
|
|
30
|
+
if (typeof part === 'string' || part.kind === 'pound')
|
|
31
|
+
return false;
|
|
32
|
+
if (part.kind === 'arg')
|
|
33
|
+
return part.name === name;
|
|
34
|
+
if (part.kind === 'plural' && part.name === name && Object.values(part.options).some(pound))
|
|
35
|
+
return true;
|
|
36
|
+
return Object.values(part.options).some(walk);
|
|
37
|
+
});
|
|
38
|
+
return walk(parseMessage(source));
|
|
39
|
+
}
|
|
40
|
+
/** Checks the file envelope (locale, names, status, reviewers, messages map). */
|
|
41
|
+
export function checkShape(input, file = '?') {
|
|
42
|
+
const issues = [];
|
|
43
|
+
const err = (message) => issues.push({ level: 'error', locale: file, message });
|
|
44
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
45
|
+
err('a catalog is a JSON object');
|
|
46
|
+
return issues;
|
|
47
|
+
}
|
|
48
|
+
const catalog = input;
|
|
49
|
+
const allowed = new Set(['$schema', 'locale', 'name', 'englishName', 'dir', 'status', 'reviewers', 'fallback', 'messages']);
|
|
50
|
+
for (const key of Object.keys(catalog))
|
|
51
|
+
if (!allowed.has(key))
|
|
52
|
+
err(`unknown field "${key}"`);
|
|
53
|
+
if (typeof catalog.locale !== 'string' || normalizeLocale(catalog.locale) !== catalog.locale)
|
|
54
|
+
err(`"locale" must be a canonical BCP 47 tag (got ${JSON.stringify(catalog.locale)})`);
|
|
55
|
+
if (typeof catalog.name !== 'string' || !catalog.name.trim())
|
|
56
|
+
err('"name" (the language\'s own name) is required');
|
|
57
|
+
if (typeof catalog.englishName !== 'string' || !catalog.englishName.trim())
|
|
58
|
+
err('"englishName" is required');
|
|
59
|
+
if (typeof catalog.status !== 'string' || !STATUSES.has(catalog.status))
|
|
60
|
+
err('"status" must be "source", "machine" or "reviewed"');
|
|
61
|
+
if (catalog.status === 'reviewed' && (!Array.isArray(catalog.reviewers) || catalog.reviewers.length === 0))
|
|
62
|
+
err('a "reviewed" catalog names its reviewers');
|
|
63
|
+
if (catalog.reviewers !== undefined && (!Array.isArray(catalog.reviewers) || catalog.reviewers.some(r => typeof r !== 'string' || !r.trim() || r.length > 80)))
|
|
64
|
+
err('"reviewers" is a list of names or handles');
|
|
65
|
+
if (catalog.dir !== undefined && catalog.dir !== 'ltr' && catalog.dir !== 'rtl')
|
|
66
|
+
err('"dir" must be "ltr" or "rtl"');
|
|
67
|
+
if (typeof catalog.locale === 'string' && catalog.dir && catalog.dir !== direction(catalog.locale))
|
|
68
|
+
err(`"dir" says ${catalog.dir} but ${catalog.locale} is written ${direction(catalog.locale)}`);
|
|
69
|
+
if (catalog.fallback !== undefined && (typeof catalog.fallback !== 'string' || normalizeLocale(catalog.fallback) !== catalog.fallback))
|
|
70
|
+
err('"fallback" must be a canonical BCP 47 tag');
|
|
71
|
+
if (!catalog.messages || typeof catalog.messages !== 'object' || Array.isArray(catalog.messages)) {
|
|
72
|
+
err('"messages" must be an object of key -> string');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
for (const [key, value] of Object.entries(catalog.messages)) {
|
|
76
|
+
if (!KEY.test(key) || key.length > 96)
|
|
77
|
+
err(`invalid key ${JSON.stringify(key)} (dotted camelCase, at most 96 characters)`);
|
|
78
|
+
if (typeof value !== 'string')
|
|
79
|
+
err(`${key}: value must be a string`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return issues;
|
|
83
|
+
}
|
|
84
|
+
/** Safety rules every message obeys, source included. */
|
|
85
|
+
export function checkMessageSafety(locale, key, value) {
|
|
86
|
+
const issues = [];
|
|
87
|
+
if (MARKUP.test(value))
|
|
88
|
+
issues.push({ level: 'error', locale, key, message: 'markup is not allowed (catalogs are text, never HTML)' });
|
|
89
|
+
if (CONTROL.test(value))
|
|
90
|
+
issues.push({ level: 'error', locale, key, message: 'control or bidi-override characters are not allowed' });
|
|
91
|
+
if (value.length > 2000)
|
|
92
|
+
issues.push({ level: 'error', locale, key, message: 'message is longer than 2000 characters' });
|
|
93
|
+
try {
|
|
94
|
+
parseMessage(value);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
issues.push({ level: 'error', locale, key, message: `invalid message syntax: ${error instanceof Error ? error.message : String(error)}` });
|
|
98
|
+
}
|
|
99
|
+
return issues;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Checks one translated catalog against the source messages: extra keys, placeholder parity,
|
|
103
|
+
* plural categories, safety, untranslated and overlong messages.
|
|
104
|
+
*/
|
|
105
|
+
export function checkCatalog(catalog, source, meta = {}) {
|
|
106
|
+
const issues = [];
|
|
107
|
+
const locale = catalog.locale;
|
|
108
|
+
const push = (level, key, message) => issues.push({ level, locale, key, message });
|
|
109
|
+
const categories = new Set(pluralCategories(locale));
|
|
110
|
+
const ordinals = new Set(pluralCategories(locale, true));
|
|
111
|
+
let missing = 0;
|
|
112
|
+
for (const [key, value] of Object.entries(catalog.messages)) {
|
|
113
|
+
const english = source[key];
|
|
114
|
+
if (english === undefined) {
|
|
115
|
+
push('error', key, 'key is not in the source catalog (removed or misspelled)');
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const safety = checkMessageSafety(locale, key, value);
|
|
119
|
+
issues.push(...safety);
|
|
120
|
+
if (safety.some(issue => issue.level === 'error'))
|
|
121
|
+
continue;
|
|
122
|
+
const entry = meta[key];
|
|
123
|
+
const allowEmpty = typeof entry === 'object' && entry.allowEmpty === true;
|
|
124
|
+
if (!value.trim() && english.trim() && !allowEmpty)
|
|
125
|
+
push('error', key, 'empty translation (remove the key to fall back to English)');
|
|
126
|
+
const want = placeholders(english);
|
|
127
|
+
const have = placeholders(value);
|
|
128
|
+
const sourceShapes = argumentShapes(english);
|
|
129
|
+
// An argument English only uses to pick a plural form ("Download {count, plural, one {it} other
|
|
130
|
+
// {them}}") may be dropped by a language that does not inflect there; every other placeholder
|
|
131
|
+
// carries text the reader needs and is required.
|
|
132
|
+
const pluralOnly = (name) => {
|
|
133
|
+
const shape = sourceShapes[name];
|
|
134
|
+
return !!shape && shape.kind !== 'select' && !usedAsText(english, name);
|
|
135
|
+
};
|
|
136
|
+
const missingNames = want.filter(name => !have.includes(name) && !pluralOnly(name));
|
|
137
|
+
const extraNames = have.filter(name => !want.includes(name));
|
|
138
|
+
if (missingNames.length)
|
|
139
|
+
push('error', key, `missing placeholder(s) ${missingNames.map(n => `{${n}}`).join(', ')}`);
|
|
140
|
+
if (extraNames.length)
|
|
141
|
+
push('error', key, `unknown placeholder(s) ${extraNames.map(n => `{${n}}`).join(', ')}`);
|
|
142
|
+
const shapes = argumentShapes(value);
|
|
143
|
+
for (const [name, shape] of Object.entries(shapes)) {
|
|
144
|
+
const expected = sourceShapes[name];
|
|
145
|
+
if (expected && expected.kind !== shape.kind) {
|
|
146
|
+
push('error', key, `{${name}} is a ${expected.kind} in English but a ${shape.kind} here`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (shape.kind === 'select') {
|
|
150
|
+
for (const option of expected?.options ?? []) {
|
|
151
|
+
if (!shape.options.includes(option))
|
|
152
|
+
push('error', key, `{${name}, select} lacks the "${option}" option`);
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// A form the translation leaves out falls back to its `other` branch, which is often right
|
|
157
|
+
// (an invariable word, an abbreviation, `=1` already covering "one"): a warning for review.
|
|
158
|
+
const needed = shape.kind === 'selectordinal' ? ordinals : categories;
|
|
159
|
+
for (const category of needed) {
|
|
160
|
+
if (!shape.options.includes(category))
|
|
161
|
+
push('warning', key, `{${name}, ${shape.kind}} has no "${category}" form (${locale} uses it; "other" is shown)`);
|
|
162
|
+
}
|
|
163
|
+
for (const option of shape.options) {
|
|
164
|
+
if (!option.startsWith('=') && !needed.has(option))
|
|
165
|
+
push('warning', key, `{${name}, ${shape.kind}} has a "${option}" form ${locale} never uses`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const maxLength = typeof entry === 'object' ? entry.maxLength : undefined;
|
|
169
|
+
const limit = maxLength ?? Math.round(english.length * LENGTH_FACTOR + LENGTH_SLACK);
|
|
170
|
+
if (value.length > limit)
|
|
171
|
+
push(maxLength ? 'error' : 'warning', key, `translation is ${value.length} characters (${maxLength ? 'limit' : 'English is'} ${maxLength ?? english.length})`);
|
|
172
|
+
}
|
|
173
|
+
if (catalog.status !== 'source' && !catalog.fallback) {
|
|
174
|
+
for (const key of Object.keys(source))
|
|
175
|
+
if (!(key in catalog.messages))
|
|
176
|
+
missing += 1;
|
|
177
|
+
if (missing)
|
|
178
|
+
push('warning', undefined, `${missing} of ${Object.keys(source).length} messages untranslated (English is shown)`);
|
|
179
|
+
}
|
|
180
|
+
return issues;
|
|
181
|
+
}
|
|
182
|
+
/** Checks the source catalog itself: safety, syntax, and that every key is described in the meta file. */
|
|
183
|
+
export function checkSource(catalog, meta = {}) {
|
|
184
|
+
const issues = [];
|
|
185
|
+
for (const [key, value] of Object.entries(catalog.messages))
|
|
186
|
+
issues.push(...checkMessageSafety(catalog.locale, key, value));
|
|
187
|
+
for (const key of Object.keys(meta)) {
|
|
188
|
+
if (!(key in catalog.messages))
|
|
189
|
+
issues.push({ level: 'error', locale: catalog.locale, key, message: 'described in the meta file but not in the source catalog' });
|
|
190
|
+
}
|
|
191
|
+
return issues;
|
|
192
|
+
}
|
|
193
|
+
/** Share of source keys a catalog translates (0..1), following its `fallback` catalog when given. */
|
|
194
|
+
export function coverage(catalog, source, fallback) {
|
|
195
|
+
const keys = Object.keys(source);
|
|
196
|
+
if (!keys.length)
|
|
197
|
+
return 1;
|
|
198
|
+
const done = keys.filter(key => key in catalog.messages || (fallback ? key in fallback.messages : false)).length;
|
|
199
|
+
return done / keys.length;
|
|
200
|
+
}
|
|
201
|
+
/** TypeScript source for a key union and per-key parameter types, generated from source messages. */
|
|
202
|
+
export function generateKeyTypes(source, typeName = 'MessageKey') {
|
|
203
|
+
const keys = Object.keys(source).sort();
|
|
204
|
+
const lines = [
|
|
205
|
+
'// Generated from the English source catalog. Do not edit by hand.',
|
|
206
|
+
`export type ${typeName} =`,
|
|
207
|
+
...keys.map((key, index) => ` | ${JSON.stringify(key)}${index === keys.length - 1 ? ';' : ''}`),
|
|
208
|
+
'',
|
|
209
|
+
`export type ${typeName}Params = {`,
|
|
210
|
+
];
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
let names = [];
|
|
213
|
+
try {
|
|
214
|
+
names = placeholders(source[key]);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
names = [];
|
|
218
|
+
}
|
|
219
|
+
lines.push(` ${JSON.stringify(key)}: ${names.length ? `{ ${names.map(name => `${name}: string | number | Date`).join('; ')} }` : 'undefined'};`);
|
|
220
|
+
}
|
|
221
|
+
lines.push('};', '');
|
|
222
|
+
return lines.join('\n');
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=i18nCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18nCheck.js","sourceRoot":"","sources":["../../../runtime/src/i18nCheck.ts"],"names":[],"mappings":"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","sourcesContent":["// Catalog checks for translators and CI (published as `waybetter/i18n/check`): every rule a\n// contributed `locales/<bcp47>.json` must pass before review. Pure functions over parsed JSON, so\n// an app's `scripts/i18n-check.mjs`, its tests and editors can share them.\n//\n// Errors fail the check: bad file shape, keys the source does not have, malformed ICU, placeholder\n// names English does not have, a missing placeholder (except an argument English only uses to choose\n// a plural form, which languages without that inflection may drop), select arguments missing an\n// English option, markup or control characters (catalogs are data, never HTML), empty translations\n// (unless the meta file allows it for a sentence fragment). Warnings do not fail: plural forms the\n// language uses but the translation leaves to `other`, untranslated keys (they fall back to\n// English) and translations much longer than the source.\n\nimport { argumentShapes, direction, normalizeLocale, parseMessage, placeholders, pluralCategories, type Catalog } from './i18n.ts';\n\nexport type CatalogIssue = { level: 'error' | 'warning'; locale: string; key?: string; message: string };\n\n/** `allowEmpty`: a sentence fragment some languages leave empty (word order moves it elsewhere). */\nexport type MetaEntry = { description?: string; maxLength?: number; context?: string; allowEmpty?: boolean };\nexport type Meta = Record<string, MetaEntry | string>;\n\nconst KEY = /^[a-z][A-Za-z0-9]*(?:[._-][A-Za-z0-9]+)*$/;\n/** Markup or script-ish content: tags, entities that decode to markup, `javascript:` and friends. */\nconst MARKUP = /<\\s*\\/?\\s*[a-z!?]|&(?:lt|gt|#0*60|#x0*3c);|javascript:|data:text\\/html/i;\nconst CONTROL = /[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f--]/;\nconst STATUSES = new Set(['source', 'machine', 'reviewed']);\n\n/** A long translation is fine up to this factor of the source length (plus some slack). */\nconst LENGTH_FACTOR = 1.8;\nconst LENGTH_SLACK = 16;\n\ntype Parts = ReturnType<typeof parseMessage>;\n\n/**\n * Whether `name` puts text in the message: a plain `{name}` / `{name, number}` somewhere, or a `#`\n * (the number itself) in one of its own plural branches. A plural argument without either only\n * chooses a form.\n */\nfunction usedAsText(source: string, name: string): boolean {\n const pound = (parts: Parts): boolean => parts.some(part => typeof part !== 'string' && (part.kind === 'pound'\n || (part.kind === 'select' && Object.values(part.options).some(pound))));\n const walk = (parts: Parts): boolean => parts.some(part => {\n if (typeof part === 'string' || part.kind === 'pound') return false;\n if (part.kind === 'arg') return part.name === name;\n if (part.kind === 'plural' && part.name === name && Object.values(part.options).some(pound)) return true;\n return Object.values(part.options).some(walk);\n });\n return walk(parseMessage(source));\n}\n\n/** Checks the file envelope (locale, names, status, reviewers, messages map). */\nexport function checkShape(input: unknown, file = '?'): CatalogIssue[] {\n const issues: CatalogIssue[] = [];\n const err = (message: string) => issues.push({ level: 'error', locale: file, message });\n if (!input || typeof input !== 'object' || Array.isArray(input)) {\n err('a catalog is a JSON object');\n return issues;\n }\n const catalog = input as Partial<Catalog> & Record<string, unknown>;\n const allowed = new Set(['$schema', 'locale', 'name', 'englishName', 'dir', 'status', 'reviewers', 'fallback', 'messages']);\n for (const key of Object.keys(catalog)) if (!allowed.has(key)) err(`unknown field \"${key}\"`);\n if (typeof catalog.locale !== 'string' || normalizeLocale(catalog.locale) !== catalog.locale) err(`\"locale\" must be a canonical BCP 47 tag (got ${JSON.stringify(catalog.locale)})`);\n if (typeof catalog.name !== 'string' || !catalog.name.trim()) err('\"name\" (the language\\'s own name) is required');\n if (typeof catalog.englishName !== 'string' || !catalog.englishName.trim()) err('\"englishName\" is required');\n if (typeof catalog.status !== 'string' || !STATUSES.has(catalog.status)) err('\"status\" must be \"source\", \"machine\" or \"reviewed\"');\n if (catalog.status === 'reviewed' && (!Array.isArray(catalog.reviewers) || catalog.reviewers.length === 0)) err('a \"reviewed\" catalog names its reviewers');\n if (catalog.reviewers !== undefined && (!Array.isArray(catalog.reviewers) || catalog.reviewers.some(r => typeof r !== 'string' || !r.trim() || r.length > 80))) err('\"reviewers\" is a list of names or handles');\n if (catalog.dir !== undefined && catalog.dir !== 'ltr' && catalog.dir !== 'rtl') err('\"dir\" must be \"ltr\" or \"rtl\"');\n if (typeof catalog.locale === 'string' && catalog.dir && catalog.dir !== direction(catalog.locale)) err(`\"dir\" says ${catalog.dir} but ${catalog.locale} is written ${direction(catalog.locale)}`);\n if (catalog.fallback !== undefined && (typeof catalog.fallback !== 'string' || normalizeLocale(catalog.fallback) !== catalog.fallback)) err('\"fallback\" must be a canonical BCP 47 tag');\n if (!catalog.messages || typeof catalog.messages !== 'object' || Array.isArray(catalog.messages)) {\n err('\"messages\" must be an object of key -> string');\n } else {\n for (const [key, value] of Object.entries(catalog.messages)) {\n if (!KEY.test(key) || key.length > 96) err(`invalid key ${JSON.stringify(key)} (dotted camelCase, at most 96 characters)`);\n if (typeof value !== 'string') err(`${key}: value must be a string`);\n }\n }\n return issues;\n}\n\n/** Safety rules every message obeys, source included. */\nexport function checkMessageSafety(locale: string, key: string, value: string): CatalogIssue[] {\n const issues: CatalogIssue[] = [];\n if (MARKUP.test(value)) issues.push({ level: 'error', locale, key, message: 'markup is not allowed (catalogs are text, never HTML)' });\n if (CONTROL.test(value)) issues.push({ level: 'error', locale, key, message: 'control or bidi-override characters are not allowed' });\n if (value.length > 2000) issues.push({ level: 'error', locale, key, message: 'message is longer than 2000 characters' });\n try {\n parseMessage(value);\n } catch (error) {\n issues.push({ level: 'error', locale, key, message: `invalid message syntax: ${error instanceof Error ? error.message : String(error)}` });\n }\n return issues;\n}\n\n/**\n * Checks one translated catalog against the source messages: extra keys, placeholder parity,\n * plural categories, safety, untranslated and overlong messages.\n */\nexport function checkCatalog(catalog: Catalog, source: Record<string, string>, meta: Meta = {}): CatalogIssue[] {\n const issues: CatalogIssue[] = [];\n const locale = catalog.locale;\n const push = (level: CatalogIssue['level'], key: string | undefined, message: string) => issues.push({ level, locale, key, message });\n const categories = new Set(pluralCategories(locale));\n const ordinals = new Set(pluralCategories(locale, true));\n let missing = 0;\n for (const [key, value] of Object.entries(catalog.messages)) {\n const english = source[key];\n if (english === undefined) {\n push('error', key, 'key is not in the source catalog (removed or misspelled)');\n continue;\n }\n const safety = checkMessageSafety(locale, key, value);\n issues.push(...safety);\n if (safety.some(issue => issue.level === 'error')) continue;\n const entry = meta[key];\n const allowEmpty = typeof entry === 'object' && entry.allowEmpty === true;\n if (!value.trim() && english.trim() && !allowEmpty) push('error', key, 'empty translation (remove the key to fall back to English)');\n const want = placeholders(english);\n const have = placeholders(value);\n const sourceShapes = argumentShapes(english);\n // An argument English only uses to pick a plural form (\"Download {count, plural, one {it} other\n // {them}}\") may be dropped by a language that does not inflect there; every other placeholder\n // carries text the reader needs and is required.\n const pluralOnly = (name: string) => {\n const shape = sourceShapes[name];\n return !!shape && shape.kind !== 'select' && !usedAsText(english, name);\n };\n const missingNames = want.filter(name => !have.includes(name) && !pluralOnly(name));\n const extraNames = have.filter(name => !want.includes(name));\n if (missingNames.length) push('error', key, `missing placeholder(s) ${missingNames.map(n => `{${n}}`).join(', ')}`);\n if (extraNames.length) push('error', key, `unknown placeholder(s) ${extraNames.map(n => `{${n}}`).join(', ')}`);\n const shapes = argumentShapes(value);\n for (const [name, shape] of Object.entries(shapes)) {\n const expected = sourceShapes[name];\n if (expected && expected.kind !== shape.kind) {\n push('error', key, `{${name}} is a ${expected.kind} in English but a ${shape.kind} here`);\n continue;\n }\n if (shape.kind === 'select') {\n for (const option of expected?.options ?? []) {\n if (!shape.options.includes(option)) push('error', key, `{${name}, select} lacks the \"${option}\" option`);\n }\n continue;\n }\n // A form the translation leaves out falls back to its `other` branch, which is often right\n // (an invariable word, an abbreviation, `=1` already covering \"one\"): a warning for review.\n const needed = shape.kind === 'selectordinal' ? ordinals : categories;\n for (const category of needed) {\n if (!shape.options.includes(category)) push('warning', key, `{${name}, ${shape.kind}} has no \"${category}\" form (${locale} uses it; \"other\" is shown)`);\n }\n for (const option of shape.options) {\n if (!option.startsWith('=') && !needed.has(option as Intl.LDMLPluralRule)) push('warning', key, `{${name}, ${shape.kind}} has a \"${option}\" form ${locale} never uses`);\n }\n }\n const maxLength = typeof entry === 'object' ? entry.maxLength : undefined;\n const limit = maxLength ?? Math.round(english.length * LENGTH_FACTOR + LENGTH_SLACK);\n if (value.length > limit) push(maxLength ? 'error' : 'warning', key, `translation is ${value.length} characters (${maxLength ? 'limit' : 'English is'} ${maxLength ?? english.length})`);\n }\n if (catalog.status !== 'source' && !catalog.fallback) {\n for (const key of Object.keys(source)) if (!(key in catalog.messages)) missing += 1;\n if (missing) push('warning', undefined, `${missing} of ${Object.keys(source).length} messages untranslated (English is shown)`);\n }\n return issues;\n}\n\n/** Checks the source catalog itself: safety, syntax, and that every key is described in the meta file. */\nexport function checkSource(catalog: Catalog, meta: Meta = {}): CatalogIssue[] {\n const issues: CatalogIssue[] = [];\n for (const [key, value] of Object.entries(catalog.messages)) issues.push(...checkMessageSafety(catalog.locale, key, value));\n for (const key of Object.keys(meta)) {\n if (!(key in catalog.messages)) issues.push({ level: 'error', locale: catalog.locale, key, message: 'described in the meta file but not in the source catalog' });\n }\n return issues;\n}\n\n/** Share of source keys a catalog translates (0..1), following its `fallback` catalog when given. */\nexport function coverage(catalog: Catalog, source: Record<string, string>, fallback?: Catalog): number {\n const keys = Object.keys(source);\n if (!keys.length) return 1;\n const done = keys.filter(key => key in catalog.messages || (fallback ? key in fallback.messages : false)).length;\n return done / keys.length;\n}\n\n/** TypeScript source for a key union and per-key parameter types, generated from source messages. */\nexport function generateKeyTypes(source: Record<string, string>, typeName = 'MessageKey'): string {\n const keys = Object.keys(source).sort();\n const lines = [\n '// Generated from the English source catalog. Do not edit by hand.',\n `export type ${typeName} =`,\n ...keys.map((key, index) => ` | ${JSON.stringify(key)}${index === keys.length - 1 ? ';' : ''}`),\n '',\n `export type ${typeName}Params = {`,\n ];\n for (const key of keys) {\n let names: string[] = [];\n try { names = placeholders(source[key]!); } catch { names = []; }\n lines.push(` ${JSON.stringify(key)}: ${names.length ? `{ ${names.map(name => `${name}: string | number | Date`).join('; ')} }` : 'undefined'};`);\n }\n lines.push('};', '');\n return lines.join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Invoke = (command: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
2
|
+
/** The page's language as a canonical tag, or null when it declares none (or nonsense). */
|
|
3
|
+
export declare function readPageLang(doc: Document): string | null;
|
|
4
|
+
/** Retries of a report that failed (the IPC bridge is not there yet at document start). */
|
|
5
|
+
export declare const LANG_REPORT_RETRIES = 12;
|
|
6
|
+
export declare function installPageLangReport(win: Window & typeof globalThis, invoke?: Invoke): {
|
|
7
|
+
dispose(): void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Tells the app which language the site's own UI is in (its `<html lang>`), so the app can follow
|
|
2
|
+
// it ("Follow X" in Settings → Language). Read from the document the site renders anyway; nothing is
|
|
3
|
+
// requested. Only a change is reported (`tab_report { lang }`); native code accepts it only from a
|
|
4
|
+
// site tab and only as a validated language tag.
|
|
5
|
+
import { normalizeLocale } from "./i18n.js";
|
|
6
|
+
/** The page's language as a canonical tag, or null when it declares none (or nonsense). */
|
|
7
|
+
export function readPageLang(doc) {
|
|
8
|
+
return normalizeLocale(doc.documentElement?.getAttribute('lang') ?? '');
|
|
9
|
+
}
|
|
10
|
+
/** Retries of a report that failed (the IPC bridge is not there yet at document start). */
|
|
11
|
+
export const LANG_REPORT_RETRIES = 12;
|
|
12
|
+
export function installPageLangReport(win, invoke) {
|
|
13
|
+
if (!invoke)
|
|
14
|
+
return { dispose() { } };
|
|
15
|
+
let last = '';
|
|
16
|
+
let failures = 0;
|
|
17
|
+
let timer;
|
|
18
|
+
let disposed = false;
|
|
19
|
+
const check = () => {
|
|
20
|
+
timer = undefined;
|
|
21
|
+
if (disposed)
|
|
22
|
+
return;
|
|
23
|
+
const lang = readPageLang(win.document);
|
|
24
|
+
if (!lang || lang === last)
|
|
25
|
+
return;
|
|
26
|
+
last = lang;
|
|
27
|
+
invoke('tab_report', { report: { lang } }).then(() => { failures = 0; }, () => {
|
|
28
|
+
// The page's `lang` rarely changes after load, so a report lost at document start (no bridge
|
|
29
|
+
// yet) would never be sent again: "Follow X" would not learn X's language. Try again.
|
|
30
|
+
if (last === lang)
|
|
31
|
+
last = '';
|
|
32
|
+
if (disposed || timer !== undefined || failures >= LANG_REPORT_RETRIES)
|
|
33
|
+
return;
|
|
34
|
+
failures += 1;
|
|
35
|
+
timer = win.setTimeout(check, Math.min(4000, 125 * 2 ** failures));
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const observer = new win.MutationObserver(check);
|
|
39
|
+
let observed = null;
|
|
40
|
+
// At document start (where the app installs this) there may be no <html> element yet: watch it,
|
|
41
|
+
// and read `lang`, as soon as the parser has made it.
|
|
42
|
+
const attach = () => {
|
|
43
|
+
const root = win.document.documentElement;
|
|
44
|
+
if (root && root !== observed) {
|
|
45
|
+
observed = root;
|
|
46
|
+
observer.observe(root, { attributes: true, attributeFilter: ['lang'] });
|
|
47
|
+
}
|
|
48
|
+
check();
|
|
49
|
+
};
|
|
50
|
+
win.document.addEventListener('readystatechange', attach);
|
|
51
|
+
win.document.addEventListener('DOMContentLoaded', attach);
|
|
52
|
+
attach();
|
|
53
|
+
return {
|
|
54
|
+
dispose: () => {
|
|
55
|
+
disposed = true;
|
|
56
|
+
observer.disconnect();
|
|
57
|
+
win.document.removeEventListener('readystatechange', attach);
|
|
58
|
+
win.document.removeEventListener('DOMContentLoaded', attach);
|
|
59
|
+
if (timer !== undefined)
|
|
60
|
+
win.clearTimeout(timer);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=pageLang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pageLang.js","sourceRoot":"","sources":["../../../runtime/src/pageLang.ts"],"names":[],"mappings":"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","sourcesContent":["// Tells the app which language the site's own UI is in (its `<html lang>`), so the app can follow\n// it (\"Follow X\" in Settings → Language). Read from the document the site renders anyway; nothing is\n// requested. Only a change is reported (`tab_report { lang }`); native code accepts it only from a\n// site tab and only as a validated language tag.\n\nimport { normalizeLocale } from './i18n.ts';\n\nexport type Invoke = (command: string, args: Record<string, unknown>) => Promise<unknown>;\n\n/** The page's language as a canonical tag, or null when it declares none (or nonsense). */\nexport function readPageLang(doc: Document): string | null {\n return normalizeLocale(doc.documentElement?.getAttribute('lang') ?? '');\n}\n\n/** Retries of a report that failed (the IPC bridge is not there yet at document start). */\nexport const LANG_REPORT_RETRIES = 12;\n\nexport function installPageLangReport(win: Window & typeof globalThis, invoke?: Invoke): { dispose(): void } {\n if (!invoke) return { dispose() {} };\n let last = '';\n let failures = 0;\n let timer: number | undefined;\n let disposed = false;\n const check = () => {\n timer = undefined;\n if (disposed) return;\n const lang = readPageLang(win.document);\n if (!lang || lang === last) return;\n last = lang;\n invoke('tab_report', { report: { lang } }).then(() => { failures = 0; }, () => {\n // The page's `lang` rarely changes after load, so a report lost at document start (no bridge\n // yet) would never be sent again: \"Follow X\" would not learn X's language. Try again.\n if (last === lang) last = '';\n if (disposed || timer !== undefined || failures >= LANG_REPORT_RETRIES) return;\n failures += 1;\n timer = win.setTimeout(check, Math.min(4000, 125 * 2 ** failures));\n });\n };\n const observer = new win.MutationObserver(check);\n let observed: Element | null = null;\n // At document start (where the app installs this) there may be no <html> element yet: watch it,\n // and read `lang`, as soon as the parser has made it.\n const attach = () => {\n const root = win.document.documentElement;\n if (root && root !== observed) {\n observed = root;\n observer.observe(root, { attributes: true, attributeFilter: ['lang'] });\n }\n check();\n };\n win.document.addEventListener('readystatechange', attach);\n win.document.addEventListener('DOMContentLoaded', attach);\n attach();\n return {\n dispose: () => {\n disposed = true;\n observer.disconnect();\n win.document.removeEventListener('readystatechange', attach);\n win.document.removeEventListener('DOMContentLoaded', attach);\n if (timer !== undefined) win.clearTimeout(timer);\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Params } from './i18n.ts';
|
|
2
|
+
import { type I18nSource } from './pluginI18n.ts';
|
|
3
|
+
export type PageStringsOptions = {
|
|
4
|
+
/** English for every string the page shows (the app's page strings and the framework's kit words). */
|
|
5
|
+
english: Readonly<Record<string, string>>;
|
|
6
|
+
/** Key prefixes a delivery may carry (`['x.', 'kit.', 'plugin.']`); other keys are dropped. */
|
|
7
|
+
prefixes: readonly string[];
|
|
8
|
+
/** Console label (the app's name). */
|
|
9
|
+
label: string;
|
|
10
|
+
/**
|
|
11
|
+
* The file-name prefix the app installs its bundled plugins with (`bt-` for `bt-<name>.plugin.js`)
|
|
12
|
+
* when their strings are keyed by the source name (`plugin.<name>.*`).
|
|
13
|
+
*/
|
|
14
|
+
bundledPrefix?: string;
|
|
15
|
+
};
|
|
16
|
+
export type PageStrings = {
|
|
17
|
+
/** The active language's message for `key`, else English. */
|
|
18
|
+
message(key: string): string | undefined;
|
|
19
|
+
/** A runtime string: `t('x.media.download')`. Placeholder values are inserted as text. */
|
|
20
|
+
t(key: string, params?: Params): string;
|
|
21
|
+
locale(): string;
|
|
22
|
+
/** Native code delivers `{ locale, dir, messages }`; anything malformed is ignored (English stays). */
|
|
23
|
+
set(value: unknown): void;
|
|
24
|
+
onChange(listener: () => void): () => void;
|
|
25
|
+
/** The plugin API's view of the language (pluginI18n.ts): `api.i18n` and the UI kit's words. */
|
|
26
|
+
source: I18nSource;
|
|
27
|
+
/** A plugin file's catalog namespace (`plugin.<namespace>.*`). */
|
|
28
|
+
pluginNamespace(id: string): string;
|
|
29
|
+
};
|
|
30
|
+
/** A delivery holds at most this many messages, each at most this long. */
|
|
31
|
+
export declare const PAGE_STRING_LIMITS: Readonly<{
|
|
32
|
+
keys: 4000;
|
|
33
|
+
value: 2000;
|
|
34
|
+
}>;
|
|
35
|
+
export declare function createPageStrings(options: PageStringsOptions): PageStrings;
|