@canopy-iiif/app 1.9.14 → 1.9.15
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/lib/build/iiif.js +13 -1
- package/lib/build/mdx.js +5 -1
- package/lib/build/pages.js +5 -0
- package/lib/default-locale.js +6 -1
- package/lib/locale-runtime.js +69 -0
- package/lib/locales.js +29 -0
- package/lib/search/search-app.jsx +32 -5
- package/lib/search/search.js +5 -0
- package/package.json +1 -1
- package/ui/dist/index.mjs +716 -355
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +728 -429
- package/ui/dist/server.mjs.map +4 -4
package/lib/build/iiif.js
CHANGED
|
@@ -23,7 +23,11 @@ const {
|
|
|
23
23
|
getLocaleRouteConfig,
|
|
24
24
|
getDefaultLocaleCode,
|
|
25
25
|
} = require("../common");
|
|
26
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
readCanopyLocalesWithMessages,
|
|
28
|
+
readLocaleMessages,
|
|
29
|
+
buildLocaleRuntimeScript,
|
|
30
|
+
} = require("../locales");
|
|
27
31
|
const {resolveCanopyConfigPath} = require("../config-path");
|
|
28
32
|
const mdx = require("./mdx");
|
|
29
33
|
const {log, logLine, logResponse} = require("./log");
|
|
@@ -2141,6 +2145,10 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
2141
2145
|
if (siteLanguageToggle) {
|
|
2142
2146
|
siteContext.languageToggle = siteLanguageToggle;
|
|
2143
2147
|
}
|
|
2148
|
+
try {
|
|
2149
|
+
const localeMessages = readLocaleMessages(pageLocale);
|
|
2150
|
+
if (localeMessages) siteContext.localeMessages = localeMessages;
|
|
2151
|
+
} catch (_) {}
|
|
2144
2152
|
try {
|
|
2145
2153
|
const localeRoutes = getLocaleRouteConfig(pageLocale);
|
|
2146
2154
|
if (localeRoutes) siteContext.routes = localeRoutes;
|
|
@@ -2313,6 +2321,10 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
2313
2321
|
);
|
|
2314
2322
|
|
|
2315
2323
|
const headSegments = [head];
|
|
2324
|
+
try {
|
|
2325
|
+
const localeScript = buildLocaleRuntimeScript(pageLocale);
|
|
2326
|
+
if (localeScript) headSegments.push(localeScript);
|
|
2327
|
+
} catch (_) {}
|
|
2316
2328
|
const needsReact = !!(
|
|
2317
2329
|
needsHydrateViewer ||
|
|
2318
2330
|
needsRelated ||
|
package/lib/build/mdx.js
CHANGED
|
@@ -20,7 +20,7 @@ const globalRoot = typeof globalThis !== "undefined" ? globalThis : global;
|
|
|
20
20
|
if (globalRoot && typeof globalRoot.__canopyRequire !== "function") {
|
|
21
21
|
globalRoot.__canopyRequire = typeof require === "function" ? require : null;
|
|
22
22
|
}
|
|
23
|
-
const {readCanopyLocalesWithMessages} = require("../locales");
|
|
23
|
+
const {readCanopyLocalesWithMessages, readLocaleMessages} = require("../locales");
|
|
24
24
|
|
|
25
25
|
function getSiteLanguageToggle() {
|
|
26
26
|
try {
|
|
@@ -1083,6 +1083,10 @@ async function compileMdxFile(filePath, outPath, Layout, extraProps = {}) {
|
|
|
1083
1083
|
if (siteLanguageToggle) {
|
|
1084
1084
|
siteContext.languageToggle = siteLanguageToggle;
|
|
1085
1085
|
}
|
|
1086
|
+
try {
|
|
1087
|
+
const localeMessages = readLocaleMessages(pageLocale);
|
|
1088
|
+
if (localeMessages) siteContext.localeMessages = localeMessages;
|
|
1089
|
+
} catch (_) {}
|
|
1086
1090
|
try {
|
|
1087
1091
|
const localeRoutes = getLocaleRouteConfig(pageLocale);
|
|
1088
1092
|
if (localeRoutes) siteContext.routes = localeRoutes;
|
package/lib/build/pages.js
CHANGED
|
@@ -14,6 +14,7 @@ const {
|
|
|
14
14
|
} = require('../common');
|
|
15
15
|
const { log } = require('./log');
|
|
16
16
|
const mdx = require('./mdx');
|
|
17
|
+
const {buildLocaleRuntimeScript} = require('../locales');
|
|
17
18
|
const navigation = require('../components/navigation');
|
|
18
19
|
const referenced = require('../components/referenced');
|
|
19
20
|
|
|
@@ -325,6 +326,10 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
|
|
|
325
326
|
if (BASE_PATH) vendorTag = `<script>window.CANOPY_BASE_PATH=${JSON.stringify(BASE_PATH)}</script>` + vendorTag;
|
|
326
327
|
} catch (_) {}
|
|
327
328
|
const headSegments = [head];
|
|
329
|
+
try {
|
|
330
|
+
const localeScript = buildLocaleRuntimeScript(pageLocale);
|
|
331
|
+
if (localeScript) headSegments.push(localeScript);
|
|
332
|
+
} catch (_) {}
|
|
328
333
|
const extraScripts = [];
|
|
329
334
|
const pushClassicScript = (src) => {
|
|
330
335
|
if (!src || src === jsRel) return;
|
package/lib/default-locale.js
CHANGED
|
@@ -23,6 +23,8 @@ module.exports = {
|
|
|
23
23
|
navigation: 'navigation',
|
|
24
24
|
section_navigation: 'section navigation',
|
|
25
25
|
content_navigation: 'content navigation',
|
|
26
|
+
primary_navigation: 'Primary navigation',
|
|
27
|
+
metadata: 'metadata',
|
|
26
28
|
gallery: 'gallery',
|
|
27
29
|
gallery_thumbnails: 'gallery thumbnails',
|
|
28
30
|
map: 'map',
|
|
@@ -62,6 +64,8 @@ module.exports = {
|
|
|
62
64
|
filter_values: 'Filter {content} values',
|
|
63
65
|
clear_content_search: 'Clear {content} search',
|
|
64
66
|
none_applied: 'No {content} applied',
|
|
67
|
+
applied_count: '{count} {content} applied',
|
|
68
|
+
toggle_content: 'Toggle {content}',
|
|
65
69
|
scroll_direction_content: 'Scroll {direction} through {content}',
|
|
66
70
|
step_content: '{direction} {content}',
|
|
67
71
|
item_numbered: '{content} {index}',
|
|
@@ -74,7 +78,8 @@ module.exports = {
|
|
|
74
78
|
next: 'Next'
|
|
75
79
|
},
|
|
76
80
|
misc: {
|
|
77
|
-
referenced_by: 'Referenced by'
|
|
81
|
+
referenced_by: 'Referenced by',
|
|
82
|
+
on_this_page: 'On this page'
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const DEFAULT_LOCALE_MESSAGES = require('./default-locale');
|
|
2
|
+
|
|
3
|
+
function getGlobalScope() {
|
|
4
|
+
if (typeof globalThis !== 'undefined') return globalThis;
|
|
5
|
+
if (typeof window !== 'undefined') return window;
|
|
6
|
+
if (typeof global !== 'undefined') return global;
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function accessPath(obj, path) {
|
|
11
|
+
if (!obj || typeof obj !== 'object' || !path) return undefined;
|
|
12
|
+
const parts = Array.isArray(path) ? path : String(path).split('.');
|
|
13
|
+
let current = obj;
|
|
14
|
+
for (const part of parts) {
|
|
15
|
+
if (!current || typeof current !== 'object') return undefined;
|
|
16
|
+
const key = String(part).trim();
|
|
17
|
+
if (!key || !(key in current)) return undefined;
|
|
18
|
+
current = current[key];
|
|
19
|
+
}
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function formatTemplate(template, replacements = {}) {
|
|
24
|
+
if (template == null) return template;
|
|
25
|
+
const str = String(template);
|
|
26
|
+
if (!replacements || typeof replacements !== 'object') return str;
|
|
27
|
+
return str.replace(/\{([^}]+)\}/g, (match, key) => {
|
|
28
|
+
const replacement = Object.prototype.hasOwnProperty.call(replacements, key)
|
|
29
|
+
? replacements[key]
|
|
30
|
+
: undefined;
|
|
31
|
+
if (replacement == null) return match;
|
|
32
|
+
return String(replacement);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getRuntimeLocaleMessages() {
|
|
37
|
+
const scope = getGlobalScope();
|
|
38
|
+
const candidate = scope && scope.CANOPY_LOCALE_MESSAGES;
|
|
39
|
+
if (candidate && typeof candidate === 'object') return candidate;
|
|
40
|
+
return DEFAULT_LOCALE_MESSAGES || {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getRuntimeLocaleCode() {
|
|
44
|
+
const scope = getGlobalScope();
|
|
45
|
+
const value = scope && scope.CANOPY_LOCALE_CODE;
|
|
46
|
+
return typeof value === 'string' ? value : '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getRuntimeMessage(path, fallback) {
|
|
50
|
+
const messages = getRuntimeLocaleMessages();
|
|
51
|
+
const value = accessPath(messages, path);
|
|
52
|
+
if (value == null) return fallback;
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatRuntimeMessage(path, fallback, replacements) {
|
|
57
|
+
const template = getRuntimeMessage(path, fallback);
|
|
58
|
+
if (template == null) return template;
|
|
59
|
+
return formatTemplate(template, replacements);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
getRuntimeLocaleMessages,
|
|
64
|
+
getRuntimeLocaleCode,
|
|
65
|
+
getRuntimeMessage,
|
|
66
|
+
formatRuntimeMessage,
|
|
67
|
+
formatTemplate,
|
|
68
|
+
accessPath,
|
|
69
|
+
};
|
package/lib/locales.js
CHANGED
|
@@ -111,6 +111,34 @@ function readLocaleMessages(lang) {
|
|
|
111
111
|
return deepMerge(defaultMessages, override);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function serializeForScript(value) {
|
|
115
|
+
try {
|
|
116
|
+
return JSON.stringify(value || {}).replace(/</g, '\\u003c');
|
|
117
|
+
} catch (_) {
|
|
118
|
+
return '{}';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function buildLocaleRuntimeScript(localeCode) {
|
|
123
|
+
let messages = null;
|
|
124
|
+
try {
|
|
125
|
+
messages = readLocaleMessages(localeCode);
|
|
126
|
+
} catch (_) {
|
|
127
|
+
messages = null;
|
|
128
|
+
}
|
|
129
|
+
if (!messages) {
|
|
130
|
+
try {
|
|
131
|
+
messages = readLocaleMessages(null);
|
|
132
|
+
} catch (_) {
|
|
133
|
+
messages = DEFAULT_LOCALE_MESSAGES;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const langValue = typeof localeCode === 'string' ? localeCode : '';
|
|
137
|
+
const serializedMessages = serializeForScript(messages || {});
|
|
138
|
+
const serializedCode = JSON.stringify(langValue || '').replace(/</g, '\\u003c');
|
|
139
|
+
return `<script>window.CANOPY_LOCALE_CODE=${serializedCode};window.CANOPY_LOCALE_MESSAGES=${serializedMessages};</script>`;
|
|
140
|
+
}
|
|
141
|
+
|
|
114
142
|
function normalizeRouteValue(value) {
|
|
115
143
|
if (typeof value !== 'string') return '';
|
|
116
144
|
const trimmed = value.trim();
|
|
@@ -170,5 +198,6 @@ module.exports = {
|
|
|
170
198
|
readCanopyLocalesWithMessages,
|
|
171
199
|
readLocaleMessages,
|
|
172
200
|
readLocaleRoutes,
|
|
201
|
+
buildLocaleRuntimeScript,
|
|
173
202
|
DEFAULT_LOCALE_ROUTES,
|
|
174
203
|
};
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
SearchFiltersDialog,
|
|
7
7
|
} from "@canopy-iiif/app/ui";
|
|
8
8
|
import resultTemplates from "__CANOPY_SEARCH_RESULT_TEMPLATES__";
|
|
9
|
+
import localeRuntime from "../locale-runtime.js";
|
|
9
10
|
const SITE_TITLE_FALLBACK = "Site title";
|
|
11
|
+
const {getRuntimeMessage, formatRuntimeMessage} = localeRuntime;
|
|
10
12
|
|
|
11
13
|
function readSiteTitleFromGlobals() {
|
|
12
14
|
try {
|
|
@@ -139,6 +141,16 @@ function resolveRecordHrefForLocale(record, fallbackHref) {
|
|
|
139
141
|
return first || fallbackHref;
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
function resolveTypeLabel(type) {
|
|
145
|
+
const key = String(type || "").toLowerCase();
|
|
146
|
+
if (key === "work") return getRuntimeMessage("common.types.work", "Works");
|
|
147
|
+
if (key === "page") return getRuntimeMessage("common.types.page", "Pages");
|
|
148
|
+
if (key === "docs") return getRuntimeMessage("common.types.docs", "Docs");
|
|
149
|
+
if (key === "all") return getRuntimeMessage("common.nouns.types", "types");
|
|
150
|
+
if (key === "annotation") return getRuntimeMessage("common.nouns.items", "items");
|
|
151
|
+
return key ? key.charAt(0).toUpperCase() + key.slice(1) : "";
|
|
152
|
+
}
|
|
153
|
+
|
|
142
154
|
// Lightweight IndexedDB utilities (no deps) with defensive guards
|
|
143
155
|
function hasIDB() {
|
|
144
156
|
try {
|
|
@@ -899,7 +911,12 @@ function useStore() {
|
|
|
899
911
|
|
|
900
912
|
function ResultsMount(props = {}) {
|
|
901
913
|
const {results, type, loading, query, resultSettings} = useStore();
|
|
902
|
-
if (loading)
|
|
914
|
+
if (loading)
|
|
915
|
+
return (
|
|
916
|
+
<div className="text-slate-600">
|
|
917
|
+
{formatRuntimeMessage("common.statuses.loading", "Loading…")}
|
|
918
|
+
</div>
|
|
919
|
+
);
|
|
903
920
|
const normalizedType = String(type || "").trim().toLowerCase();
|
|
904
921
|
const hasOverrides =
|
|
905
922
|
resultSettings && Object.keys(resultSettings || {}).length > 0;
|
|
@@ -977,10 +994,20 @@ function TabsMount() {
|
|
|
977
994
|
function SummaryMount() {
|
|
978
995
|
const {query, type, shown, total} = useStore();
|
|
979
996
|
const text = useMemo(() => {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
997
|
+
const itemsLabel = getRuntimeMessage("common.nouns.items", "items");
|
|
998
|
+
if (!query) {
|
|
999
|
+
return formatRuntimeMessage(
|
|
1000
|
+
"common.statuses.summary_content",
|
|
1001
|
+
"Showing {shown} of {total} {content}",
|
|
1002
|
+
{shown, total, content: itemsLabel},
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
const typeLabel = resolveTypeLabel(type);
|
|
1006
|
+
return formatRuntimeMessage(
|
|
1007
|
+
"common.statuses.search_summary",
|
|
1008
|
+
'Found {shown} of {total} in {type} for "{query}"',
|
|
1009
|
+
{shown, total, type: typeLabel, query},
|
|
1010
|
+
);
|
|
984
1011
|
}, [query, type, shown, total]);
|
|
985
1012
|
return <div className="text-sm text-slate-600">{text}</div>;
|
|
986
1013
|
}
|
package/lib/search/search.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
getDefaultLocaleCode,
|
|
19
19
|
getSiteTitle,
|
|
20
20
|
} = require('../common');
|
|
21
|
+
const {buildLocaleRuntimeScript} = require('../locales');
|
|
21
22
|
const { resolveCanopyConfigPath } = require('../config-path');
|
|
22
23
|
|
|
23
24
|
const SEARCH_TEMPLATES_ALIAS = '__CANOPY_SEARCH_RESULT_TEMPLATES__';
|
|
@@ -368,6 +369,10 @@ async function buildSearchPageForEntry(routeEntry) {
|
|
|
368
369
|
}
|
|
369
370
|
}
|
|
370
371
|
let headExtra = vendorTags + head + importMap + customRuntimeTag;
|
|
372
|
+
try {
|
|
373
|
+
const localeScript = buildLocaleRuntimeScript(pageLocale);
|
|
374
|
+
if (localeScript) headExtra = localeScript + headExtra;
|
|
375
|
+
} catch (_) {}
|
|
371
376
|
if (siteTitle && typeof siteTitle === 'string') {
|
|
372
377
|
const siteTitleScript = `<script>window.CANOPY_SITE_TITLE=${JSON.stringify(siteTitle)}</script>`;
|
|
373
378
|
headExtra = siteTitleScript + headExtra;
|