@janga/norna 0.7.24 → 0.7.26

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.
Files changed (142) hide show
  1. package/README.md +5 -4
  2. package/astro.config.mjs +27 -11
  3. package/bin/norna-cli.mjs +12 -0
  4. package/package.json +26 -5
  5. package/schemas/card-list.schema.json +121 -0
  6. package/schemas/category.schema.json +2 -2
  7. package/schemas/config.schema.json +342 -33
  8. package/schemas/content-frontmatter.schema.json +7 -7
  9. package/schemas/image-carousel.schema.json +43 -0
  10. package/schemas/image-stack.schema.json +43 -0
  11. package/schemas/manifest.json +6 -1
  12. package/schemas/page-theme.schema.json +26 -26
  13. package/schemas/sitewide-content.schema.json +18 -18
  14. package/schemas/theme.schema.json +237 -262
  15. package/scripts/check-config.mjs +5 -1
  16. package/scripts/dev-local.mjs +130 -13
  17. package/scripts/generate-schemas.mjs +15 -3
  18. package/scripts/init-site.mjs +2 -0
  19. package/scripts/lib/category-destinations.mjs +48 -0
  20. package/scripts/lib/code-fence-metadata.mjs +25 -43
  21. package/scripts/lib/content-tabs.mjs +155 -0
  22. package/scripts/lib/details-headings.mjs +58 -0
  23. package/scripts/lib/docusaurus-migration-audit.mjs +603 -0
  24. package/scripts/lib/edit-source-link.mjs +53 -0
  25. package/scripts/lib/editor-language-service.mjs +238 -116
  26. package/scripts/lib/locale-registry.mjs +597 -0
  27. package/scripts/lib/locales/az-Latn.mjs +78 -0
  28. package/scripts/lib/locales/be.mjs +78 -0
  29. package/scripts/lib/locales/bg.mjs +78 -0
  30. package/scripts/lib/locales/bs.mjs +78 -0
  31. package/scripts/lib/locales/ca.mjs +78 -0
  32. package/scripts/lib/locales/cnr.mjs +78 -0
  33. package/scripts/lib/locales/cs.mjs +78 -0
  34. package/scripts/lib/locales/da.mjs +78 -0
  35. package/scripts/lib/locales/de.mjs +78 -0
  36. package/scripts/lib/locales/el.mjs +78 -0
  37. package/scripts/lib/locales/en.mjs +78 -0
  38. package/scripts/lib/locales/es.mjs +78 -0
  39. package/scripts/lib/locales/et.mjs +78 -0
  40. package/scripts/lib/locales/fi.mjs +78 -0
  41. package/scripts/lib/locales/fil.mjs +78 -0
  42. package/scripts/lib/locales/fr.mjs +78 -0
  43. package/scripts/lib/locales/ga.mjs +78 -0
  44. package/scripts/lib/locales/ha.mjs +78 -0
  45. package/scripts/lib/locales/hr.mjs +78 -0
  46. package/scripts/lib/locales/hu.mjs +78 -0
  47. package/scripts/lib/locales/id.mjs +78 -0
  48. package/scripts/lib/locales/is.mjs +78 -0
  49. package/scripts/lib/locales/it.mjs +78 -0
  50. package/scripts/lib/locales/jv.mjs +78 -0
  51. package/scripts/lib/locales/lb.mjs +78 -0
  52. package/scripts/lib/locales/lt.mjs +78 -0
  53. package/scripts/lib/locales/lv.mjs +78 -0
  54. package/scripts/lib/locales/mk.mjs +78 -0
  55. package/scripts/lib/locales/mt.mjs +78 -0
  56. package/scripts/lib/locales/nb.mjs +78 -0
  57. package/scripts/lib/locales/nl.mjs +78 -0
  58. package/scripts/lib/locales/nn.mjs +78 -0
  59. package/scripts/lib/locales/pcm.mjs +78 -0
  60. package/scripts/lib/locales/pl.mjs +78 -0
  61. package/scripts/lib/locales/pt.mjs +78 -0
  62. package/scripts/lib/locales/ro.mjs +78 -0
  63. package/scripts/lib/locales/ru.mjs +78 -0
  64. package/scripts/lib/locales/sk.mjs +78 -0
  65. package/scripts/lib/locales/sl.mjs +78 -0
  66. package/scripts/lib/locales/sq.mjs +78 -0
  67. package/scripts/lib/locales/sr-Cyrl.mjs +78 -0
  68. package/scripts/lib/locales/sr-Latn.mjs +78 -0
  69. package/scripts/lib/locales/sv.mjs +78 -0
  70. package/scripts/lib/locales/sw.mjs +78 -0
  71. package/scripts/lib/locales/tr.mjs +78 -0
  72. package/scripts/lib/locales/uk.mjs +78 -0
  73. package/scripts/lib/locales/vi.mjs +78 -0
  74. package/scripts/lib/markdown-links.mjs +4 -25
  75. package/scripts/lib/markdown-notes-render-plugin.mjs +75 -0
  76. package/scripts/lib/markdown-notes.mjs +165 -0
  77. package/scripts/lib/navigation-model.mjs +1 -1
  78. package/scripts/lib/norna-markdown-blocks.mjs +123 -447
  79. package/scripts/lib/norna-markdown-render-plugin.mjs +4 -0
  80. package/scripts/lib/page-aliases.mjs +12 -1
  81. package/scripts/lib/page-markdown.mjs +63 -16
  82. package/scripts/lib/page-move-plan.mjs +22 -5
  83. package/scripts/lib/pagefind-translations.mjs +620 -0
  84. package/scripts/lib/presentation-contract.mjs +6 -1
  85. package/scripts/lib/presentation.mjs +29 -30
  86. package/scripts/lib/project-config.mjs +22 -163
  87. package/scripts/lib/schema-definitions.mjs +13 -12
  88. package/scripts/lib/schema-editor-metadata.mjs +42 -46
  89. package/scripts/lib/schema-value-definitions.mjs +7 -8
  90. package/scripts/lib/semantic-callouts.mjs +4 -2
  91. package/scripts/lib/site-content.mjs +4 -2
  92. package/scripts/lib/site-link-graph.mjs +16 -6
  93. package/scripts/lib/site-navigation-tree.mjs +0 -11
  94. package/scripts/lib/site-paths.mjs +14 -2
  95. package/scripts/lib/sitemap.mjs +5 -3
  96. package/scripts/lib/structured-blocks.mjs +108 -0
  97. package/scripts/lib/table-render-plugin.mjs +76 -0
  98. package/scripts/lib/table-row-headers.mjs +218 -0
  99. package/scripts/lib/theme-presets.mjs +3 -10
  100. package/scripts/lib/yaml-config.mjs +2 -6
  101. package/scripts/migrate-check.mjs +19 -0
  102. package/scripts/sync-content-sections.mjs +39 -10
  103. package/scripts/sync-site-public.mjs +7 -1
  104. package/src/components/CardList.astro +1 -0
  105. package/src/components/CodeBlockCopyScript.astro +49 -6
  106. package/src/components/ContentBlocks.astro +39 -0
  107. package/src/components/ContentTabsScript.astro +60 -0
  108. package/src/components/DisplaySettings.astro +17 -19
  109. package/src/components/ImageCarousel.astro +4 -1
  110. package/src/components/ImageStack.astro +34 -9
  111. package/src/components/ImageStackEnhancement.astro +331 -0
  112. package/src/components/NavigationPageTree.astro +1 -1
  113. package/src/components/NoteNavigationScript.astro +4 -0
  114. package/src/components/PageAliasRedirect.astro +5 -4
  115. package/src/components/PageContentsNavigation.astro +2 -3
  116. package/src/components/SearchNavigationScript.astro +4 -0
  117. package/src/components/SearchPage.astro +43 -2
  118. package/src/components/SectionNavigationScript.astro +86 -16
  119. package/src/components/SiteBreadcrumbs.astro +1 -1
  120. package/src/components/SiteNavigation.astro +98 -65
  121. package/src/components/SitePage.astro +73 -33
  122. package/src/components/SiteSection.astro +5 -81
  123. package/src/components/SiteTreeNavigation.astro +5 -2
  124. package/src/components/TableOverflowScript.astro +579 -0
  125. package/src/components/TopPageMenu.astro +47 -0
  126. package/src/components/TreeNavigationScript.astro +86 -23
  127. package/src/layouts/BaseLayout.astro +34 -12
  128. package/src/lib/generatedImages.ts +18 -0
  129. package/src/lib/navigationFollowing.ts +142 -0
  130. package/src/lib/noteNavigation.ts +50 -0
  131. package/src/lib/searchNavigation.ts +107 -0
  132. package/src/lib/sectionContent.ts +23 -116
  133. package/src/lib/siteNavigation.ts +0 -5
  134. package/src/lib/sitePages.ts +18 -4
  135. package/src/lib/table-sort-tooltip.ts +109 -0
  136. package/src/pages/[...slug].astro +16 -6
  137. package/src/styles/content.css +563 -26
  138. package/src/styles/foundations.css +9 -0
  139. package/src/styles/media.css +192 -3
  140. package/src/styles/navigation.css +214 -60
  141. package/src/styles/page-layout.css +55 -16
  142. package/src/styles/responsive.css +62 -20
@@ -0,0 +1,597 @@
1
+ import bulgarianLabels from './locales/bg.mjs';
2
+ import bosnianLabels from './locales/bs.mjs';
3
+ import catalanLabels from './locales/ca.mjs';
4
+ import montenegrinLabels from './locales/cnr.mjs';
5
+ import czechLabels from './locales/cs.mjs';
6
+ import danishLabels from './locales/da.mjs';
7
+ import germanLabels from './locales/de.mjs';
8
+ import greekLabels from './locales/el.mjs';
9
+ import englishLabels from './locales/en.mjs';
10
+ import spanishLabels from './locales/es.mjs';
11
+ import estonianLabels from './locales/et.mjs';
12
+ import filipinoLabels from './locales/fil.mjs';
13
+ import finnishLabels from './locales/fi.mjs';
14
+ import frenchLabels from './locales/fr.mjs';
15
+ import irishLabels from './locales/ga.mjs';
16
+ import hausaLabels from './locales/ha.mjs';
17
+ import croatianLabels from './locales/hr.mjs';
18
+ import hungarianLabels from './locales/hu.mjs';
19
+ import italianLabels from './locales/it.mjs';
20
+ import icelandicLabels from './locales/is.mjs';
21
+ import portugueseLabels from './locales/pt.mjs';
22
+ import indonesianLabels from './locales/id.mjs';
23
+ import javaneseLabels from './locales/jv.mjs';
24
+ import lithuanianLabels from './locales/lt.mjs';
25
+ import latvianLabels from './locales/lv.mjs';
26
+ import luxembourgishLabels from './locales/lb.mjs';
27
+ import macedonianLabels from './locales/mk.mjs';
28
+ import malteseLabels from './locales/mt.mjs';
29
+ import norwegianBokmalLabels from './locales/nb.mjs';
30
+ import norwegianNynorskLabels from './locales/nn.mjs';
31
+ import dutchLabels from './locales/nl.mjs';
32
+ import polishLabels from './locales/pl.mjs';
33
+ import pidginLabels from './locales/pcm.mjs';
34
+ import russianLabels from './locales/ru.mjs';
35
+ import romanianLabels from './locales/ro.mjs';
36
+ import slovakLabels from './locales/sk.mjs';
37
+ import slovenianLabels from './locales/sl.mjs';
38
+ import swedishLabels from './locales/sv.mjs';
39
+ import swahiliLabels from './locales/sw.mjs';
40
+ import albanianLabels from './locales/sq.mjs';
41
+ import serbianCyrillicLabels from './locales/sr-Cyrl.mjs';
42
+ import serbianLatinLabels from './locales/sr-Latn.mjs';
43
+ import turkishLabels from './locales/tr.mjs';
44
+ import ukrainianLabels from './locales/uk.mjs';
45
+ import vietnameseLabels from './locales/vi.mjs';
46
+ import azerbaijaniLatinLabels from './locales/az-Latn.mjs';
47
+ import belarusianLabels from './locales/be.mjs';
48
+ import { getPagefindTranslations } from './pagefind-translations.mjs';
49
+
50
+ const placeholderPattern = /\{([a-z]+)\}/g;
51
+ const englishLabelKeys = Object.freeze(Object.keys(englishLabels));
52
+
53
+ const getPlaceholders = (value) => [...value.matchAll(placeholderPattern)].map((match) => match[1]).sort();
54
+
55
+ const assertCompleteLabels = (tag, labels) => {
56
+ if (!labels || typeof labels !== 'object' || Array.isArray(labels)) {
57
+ throw new Error(`Locale ${tag} must provide an object of Norna interface labels.`);
58
+ }
59
+
60
+ const keys = Object.keys(labels);
61
+ const missing = englishLabelKeys.filter((key) => !Object.hasOwn(labels, key));
62
+ const unexpected = keys.filter((key) => !Object.hasOwn(englishLabels, key));
63
+ if (missing.length > 0 || unexpected.length > 0) {
64
+ throw new Error([
65
+ `Locale ${tag} does not match the Norna interface-label contract.`,
66
+ ...(missing.length > 0 ? [`Missing: ${missing.join(', ')}`] : []),
67
+ ...(unexpected.length > 0 ? [`Unexpected: ${unexpected.join(', ')}`] : []),
68
+ ].join('\n'));
69
+ }
70
+
71
+ for (const key of englishLabelKeys) {
72
+ const value = labels[key];
73
+ if (typeof value !== 'string' || value.trim() === '') {
74
+ throw new Error(`Locale ${tag} label ${key} must be a non-empty string.`);
75
+ }
76
+ const expectedPlaceholders = getPlaceholders(englishLabels[key]);
77
+ const actualPlaceholders = getPlaceholders(value);
78
+ if (expectedPlaceholders.join('\0') !== actualPlaceholders.join('\0')) {
79
+ throw new Error(`Locale ${tag} label ${key} must preserve placeholders: ${expectedPlaceholders.join(', ') || '(none)'}.`);
80
+ }
81
+ }
82
+
83
+ return Object.freeze(labels);
84
+ };
85
+
86
+ const pagefindBuiltInLanguages = new Set([
87
+ 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'fi', 'fr', 'hr', 'hu', 'id', 'it',
88
+ 'nb', 'nl', 'nn', 'pl', 'pt', 'ro', 'ru', 'sr', 'sv', 'sw', 'tr', 'uk', 'vi',
89
+ ]);
90
+
91
+ const defineLocale = ({ tag, language, name, nativeName, script, labels, requiresScript = false }) => {
92
+ const pagefindTranslations = getPagefindTranslations(tag);
93
+ if (!pagefindTranslations && !pagefindBuiltInLanguages.has(language)) {
94
+ throw new Error(`Locale ${tag} needs a complete Pagefind UI translation.`);
95
+ }
96
+
97
+ return Object.freeze({
98
+ tag,
99
+ language,
100
+ name,
101
+ nativeName,
102
+ script,
103
+ requiresScript,
104
+ labels: assertCompleteLabels(tag, labels),
105
+ pagefindTranslations,
106
+ });
107
+ };
108
+
109
+ export const localeDefinitions = Object.freeze([
110
+ defineLocale({
111
+ tag: 'en',
112
+ language: 'en',
113
+ name: 'English',
114
+ nativeName: 'English',
115
+ script: 'Latn',
116
+ labels: englishLabels,
117
+ }),
118
+ defineLocale({
119
+ tag: 'es',
120
+ language: 'es',
121
+ name: 'Spanish',
122
+ nativeName: 'Español',
123
+ script: 'Latn',
124
+ labels: spanishLabels,
125
+ }),
126
+ defineLocale({
127
+ tag: 'fr',
128
+ language: 'fr',
129
+ name: 'French',
130
+ nativeName: 'Français',
131
+ script: 'Latn',
132
+ labels: frenchLabels,
133
+ }),
134
+ defineLocale({
135
+ tag: 'pt',
136
+ language: 'pt',
137
+ name: 'Portuguese',
138
+ nativeName: 'Português',
139
+ script: 'Latn',
140
+ labels: portugueseLabels,
141
+ }),
142
+ defineLocale({
143
+ tag: 'ru',
144
+ language: 'ru',
145
+ name: 'Russian',
146
+ nativeName: 'Русский',
147
+ script: 'Cyrl',
148
+ labels: russianLabels,
149
+ }),
150
+ defineLocale({
151
+ tag: 'sw',
152
+ language: 'sw',
153
+ name: 'Swahili',
154
+ nativeName: 'Kiswahili',
155
+ script: 'Latn',
156
+ labels: swahiliLabels,
157
+ }),
158
+ defineLocale({
159
+ tag: 'jv',
160
+ language: 'jv',
161
+ name: 'Javanese',
162
+ nativeName: 'Basa Jawa',
163
+ script: 'Latn',
164
+ labels: javaneseLabels,
165
+ }),
166
+ defineLocale({
167
+ tag: 'id',
168
+ language: 'id',
169
+ name: 'Indonesian',
170
+ nativeName: 'Bahasa Indonesia',
171
+ script: 'Latn',
172
+ labels: indonesianLabels,
173
+ }),
174
+ defineLocale({
175
+ tag: 'de',
176
+ language: 'de',
177
+ name: 'German',
178
+ nativeName: 'Deutsch',
179
+ script: 'Latn',
180
+ labels: germanLabels,
181
+ }),
182
+ defineLocale({
183
+ tag: 'it',
184
+ language: 'it',
185
+ name: 'Italian',
186
+ nativeName: 'Italiano',
187
+ script: 'Latn',
188
+ labels: italianLabels,
189
+ }),
190
+ defineLocale({
191
+ tag: 'vi',
192
+ language: 'vi',
193
+ name: 'Vietnamese',
194
+ nativeName: 'Tiếng Việt',
195
+ script: 'Latn',
196
+ labels: vietnameseLabels,
197
+ }),
198
+ defineLocale({
199
+ tag: 'tr',
200
+ language: 'tr',
201
+ name: 'Turkish',
202
+ nativeName: 'Türkçe',
203
+ script: 'Latn',
204
+ labels: turkishLabels,
205
+ }),
206
+ defineLocale({
207
+ tag: 'fil',
208
+ language: 'fil',
209
+ name: 'Filipino',
210
+ nativeName: 'Filipino',
211
+ script: 'Latn',
212
+ labels: filipinoLabels,
213
+ }),
214
+ defineLocale({
215
+ tag: 'pcm',
216
+ language: 'pcm',
217
+ name: 'Nigerian Pidgin',
218
+ nativeName: 'Naijá',
219
+ script: 'Latn',
220
+ labels: pidginLabels,
221
+ }),
222
+ defineLocale({
223
+ tag: 'ha',
224
+ language: 'ha',
225
+ name: 'Hausa',
226
+ nativeName: 'Hausa',
227
+ script: 'Latn',
228
+ labels: hausaLabels,
229
+ }),
230
+ defineLocale({
231
+ tag: 'sv',
232
+ language: 'sv',
233
+ name: 'Swedish',
234
+ nativeName: 'Svenska',
235
+ script: 'Latn',
236
+ labels: swedishLabels,
237
+ }),
238
+ defineLocale({
239
+ tag: 'bg',
240
+ language: 'bg',
241
+ name: 'Bulgarian',
242
+ nativeName: 'Български',
243
+ script: 'Cyrl',
244
+ labels: bulgarianLabels,
245
+ }),
246
+ defineLocale({
247
+ tag: 'hr',
248
+ language: 'hr',
249
+ name: 'Croatian',
250
+ nativeName: 'Hrvatski',
251
+ script: 'Latn',
252
+ labels: croatianLabels,
253
+ }),
254
+ defineLocale({
255
+ tag: 'cs',
256
+ language: 'cs',
257
+ name: 'Czech',
258
+ nativeName: 'Čeština',
259
+ script: 'Latn',
260
+ labels: czechLabels,
261
+ }),
262
+ defineLocale({
263
+ tag: 'da',
264
+ language: 'da',
265
+ name: 'Danish',
266
+ nativeName: 'Dansk',
267
+ script: 'Latn',
268
+ labels: danishLabels,
269
+ }),
270
+ defineLocale({
271
+ tag: 'nl',
272
+ language: 'nl',
273
+ name: 'Dutch',
274
+ nativeName: 'Nederlands',
275
+ script: 'Latn',
276
+ labels: dutchLabels,
277
+ }),
278
+ defineLocale({
279
+ tag: 'et',
280
+ language: 'et',
281
+ name: 'Estonian',
282
+ nativeName: 'Eesti',
283
+ script: 'Latn',
284
+ labels: estonianLabels,
285
+ }),
286
+ defineLocale({
287
+ tag: 'fi',
288
+ language: 'fi',
289
+ name: 'Finnish',
290
+ nativeName: 'Suomi',
291
+ script: 'Latn',
292
+ labels: finnishLabels,
293
+ }),
294
+ defineLocale({
295
+ tag: 'el',
296
+ language: 'el',
297
+ name: 'Greek',
298
+ nativeName: 'Ελληνικά',
299
+ script: 'Grek',
300
+ labels: greekLabels,
301
+ }),
302
+ defineLocale({
303
+ tag: 'hu',
304
+ language: 'hu',
305
+ name: 'Hungarian',
306
+ nativeName: 'Magyar',
307
+ script: 'Latn',
308
+ labels: hungarianLabels,
309
+ }),
310
+ defineLocale({
311
+ tag: 'ga',
312
+ language: 'ga',
313
+ name: 'Irish',
314
+ nativeName: 'Gaeilge',
315
+ script: 'Latn',
316
+ labels: irishLabels,
317
+ }),
318
+ defineLocale({
319
+ tag: 'lv',
320
+ language: 'lv',
321
+ name: 'Latvian',
322
+ nativeName: 'Latviešu',
323
+ script: 'Latn',
324
+ labels: latvianLabels,
325
+ }),
326
+ defineLocale({
327
+ tag: 'lt',
328
+ language: 'lt',
329
+ name: 'Lithuanian',
330
+ nativeName: 'Lietuvių',
331
+ script: 'Latn',
332
+ labels: lithuanianLabels,
333
+ }),
334
+ defineLocale({
335
+ tag: 'mt',
336
+ language: 'mt',
337
+ name: 'Maltese',
338
+ nativeName: 'Malti',
339
+ script: 'Latn',
340
+ labels: malteseLabels,
341
+ }),
342
+ defineLocale({
343
+ tag: 'pl',
344
+ language: 'pl',
345
+ name: 'Polish',
346
+ nativeName: 'Polski',
347
+ script: 'Latn',
348
+ labels: polishLabels,
349
+ }),
350
+ defineLocale({
351
+ tag: 'ro',
352
+ language: 'ro',
353
+ name: 'Romanian',
354
+ nativeName: 'Română',
355
+ script: 'Latn',
356
+ labels: romanianLabels,
357
+ }),
358
+ defineLocale({
359
+ tag: 'sk',
360
+ language: 'sk',
361
+ name: 'Slovak',
362
+ nativeName: 'Slovenčina',
363
+ script: 'Latn',
364
+ labels: slovakLabels,
365
+ }),
366
+ defineLocale({
367
+ tag: 'sl',
368
+ language: 'sl',
369
+ name: 'Slovenian',
370
+ nativeName: 'Slovenščina',
371
+ script: 'Latn',
372
+ labels: slovenianLabels,
373
+ }),
374
+ defineLocale({
375
+ tag: 'sq',
376
+ language: 'sq',
377
+ name: 'Albanian',
378
+ nativeName: 'Shqip',
379
+ script: 'Latn',
380
+ labels: albanianLabels,
381
+ }),
382
+ defineLocale({
383
+ tag: 'az-Latn',
384
+ language: 'az',
385
+ name: 'Azerbaijani (Latin)',
386
+ nativeName: 'Azərbaycanca',
387
+ script: 'Latn',
388
+ requiresScript: true,
389
+ labels: azerbaijaniLatinLabels,
390
+ }),
391
+ defineLocale({
392
+ tag: 'be',
393
+ language: 'be',
394
+ name: 'Belarusian',
395
+ nativeName: 'Беларуская',
396
+ script: 'Cyrl',
397
+ labels: belarusianLabels,
398
+ }),
399
+ defineLocale({
400
+ tag: 'bs',
401
+ language: 'bs',
402
+ name: 'Bosnian',
403
+ nativeName: 'Bosanski',
404
+ script: 'Latn',
405
+ labels: bosnianLabels,
406
+ }),
407
+ defineLocale({
408
+ tag: 'ca',
409
+ language: 'ca',
410
+ name: 'Catalan',
411
+ nativeName: 'Català',
412
+ script: 'Latn',
413
+ labels: catalanLabels,
414
+ }),
415
+ defineLocale({
416
+ tag: 'is',
417
+ language: 'is',
418
+ name: 'Icelandic',
419
+ nativeName: 'Íslenska',
420
+ script: 'Latn',
421
+ labels: icelandicLabels,
422
+ }),
423
+ defineLocale({
424
+ tag: 'lb',
425
+ language: 'lb',
426
+ name: 'Luxembourgish',
427
+ nativeName: 'Lëtzebuergesch',
428
+ script: 'Latn',
429
+ labels: luxembourgishLabels,
430
+ }),
431
+ defineLocale({
432
+ tag: 'mk',
433
+ language: 'mk',
434
+ name: 'Macedonian',
435
+ nativeName: 'Македонски',
436
+ script: 'Cyrl',
437
+ labels: macedonianLabels,
438
+ }),
439
+ defineLocale({
440
+ tag: 'cnr',
441
+ language: 'cnr',
442
+ name: 'Montenegrin',
443
+ nativeName: 'Crnogorski',
444
+ script: 'Latn',
445
+ labels: montenegrinLabels,
446
+ }),
447
+ defineLocale({
448
+ tag: 'nb',
449
+ language: 'nb',
450
+ name: 'Norwegian Bokmål',
451
+ nativeName: 'Norsk bokmål',
452
+ script: 'Latn',
453
+ labels: norwegianBokmalLabels,
454
+ }),
455
+ defineLocale({
456
+ tag: 'nn',
457
+ language: 'nn',
458
+ name: 'Norwegian Nynorsk',
459
+ nativeName: 'Norsk nynorsk',
460
+ script: 'Latn',
461
+ labels: norwegianNynorskLabels,
462
+ }),
463
+ defineLocale({
464
+ tag: 'sr-Cyrl',
465
+ language: 'sr',
466
+ name: 'Serbian (Cyrillic)',
467
+ nativeName: 'Српски',
468
+ script: 'Cyrl',
469
+ requiresScript: true,
470
+ labels: serbianCyrillicLabels,
471
+ }),
472
+ defineLocale({
473
+ tag: 'sr-Latn',
474
+ language: 'sr',
475
+ name: 'Serbian (Latin)',
476
+ nativeName: 'Srpski',
477
+ script: 'Latn',
478
+ requiresScript: true,
479
+ labels: serbianLatinLabels,
480
+ }),
481
+ defineLocale({
482
+ tag: 'uk',
483
+ language: 'uk',
484
+ name: 'Ukrainian',
485
+ nativeName: 'Українська',
486
+ script: 'Cyrl',
487
+ labels: ukrainianLabels,
488
+ }),
489
+ ]);
490
+
491
+ export const supportedLocaleTags = Object.freeze(localeDefinitions.map(({ tag }) => tag));
492
+
493
+ const escapeRegularExpression = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
494
+ const regionPatternSource = String.raw`(?:[A-Za-z]{2}|[0-9]{3})`;
495
+ const definitionTagPattern = ({ tag, language, script, requiresScript }, requireQualifier = false) => {
496
+ const escapedTag = escapeRegularExpression(tag);
497
+ if (requiresScript) {
498
+ return requireQualifier
499
+ ? `${escapedTag}-${regionPatternSource}`
500
+ : `${escapedTag}(?:-${regionPatternSource})?`;
501
+ }
502
+
503
+ const escapedLanguage = escapeRegularExpression(language);
504
+ const escapedScript = escapeRegularExpression(script);
505
+ return requireQualifier
506
+ ? `${escapedLanguage}(?:-${escapedScript}(?:-${regionPatternSource})?|-${regionPatternSource})`
507
+ : `${escapedLanguage}(?:-${escapedScript})?(?:-${regionPatternSource})?`;
508
+ };
509
+
510
+ export const supportedLocaleTagPatternSource = `^(?:${localeDefinitions
511
+ .map((definition) => definitionTagPattern(definition))
512
+ .join('|')})$`;
513
+ export const supportedQualifiedLocaleTagPatternSource = `^(?:${localeDefinitions
514
+ .map((definition) => definitionTagPattern(definition, true))
515
+ .join('|')})$`;
516
+ export const supportedLocaleTagPattern = new RegExp(supportedLocaleTagPatternSource);
517
+
518
+ const definitionsByLanguage = new Map();
519
+ for (const definition of localeDefinitions) {
520
+ const definitions = definitionsByLanguage.get(definition.language) ?? [];
521
+ definitions.push(definition);
522
+ definitionsByLanguage.set(definition.language, definitions);
523
+ }
524
+
525
+ const normalizeLanguageTag = (value) => {
526
+ if (typeof value !== 'string' || value.trim() === '') {
527
+ throw new TypeError('must be a non-empty BCP 47 language tag');
528
+ }
529
+
530
+ const source = value.trim();
531
+ try {
532
+ Intl.getCanonicalLocales(source);
533
+ } catch {
534
+ throw new TypeError('must be a valid BCP 47 language tag');
535
+ }
536
+
537
+ const parts = source.split('-');
538
+ const language = parts.shift()?.toLowerCase();
539
+ let script = null;
540
+ let region = null;
541
+ const canonicalParts = [language];
542
+
543
+ if (parts[0] && /^[a-zA-Z]{4}$/.test(parts[0])) {
544
+ script = `${parts[0][0].toUpperCase()}${parts[0].slice(1).toLowerCase()}`;
545
+ canonicalParts.push(script);
546
+ parts.shift();
547
+ }
548
+ if (parts[0] && /^(?:[a-zA-Z]{2}|\d{3})$/.test(parts[0])) {
549
+ region = parts[0].toUpperCase();
550
+ canonicalParts.push(region);
551
+ parts.shift();
552
+ }
553
+ canonicalParts.push(...parts.map((part) => part.toLowerCase()));
554
+
555
+ return Object.freeze({
556
+ lang: canonicalParts.join('-'),
557
+ language,
558
+ region,
559
+ script,
560
+ });
561
+ };
562
+
563
+ const formatSupportedLanguages = () => supportedLocaleTags.join(', ');
564
+
565
+ export const resolveLocale = (configuredLanguage = 'en', sourceLabel = 'site/config.yaml') => {
566
+ let parsed;
567
+ try {
568
+ parsed = normalizeLanguageTag(configuredLanguage);
569
+ } catch (error) {
570
+ throw new Error(`language ${error.message} in ${sourceLabel}.`);
571
+ }
572
+
573
+ const candidates = definitionsByLanguage.get(parsed.language) ?? [];
574
+ if (candidates.length === 0) {
575
+ throw new Error(`language "${parsed.lang}" has no built-in Norna UI text. Supported languages: ${formatSupportedLanguages()}.`);
576
+ }
577
+
578
+ const matchingScript = parsed.script
579
+ ? candidates.find(({ script }) => script === parsed.script)
580
+ : candidates.find(({ requiresScript }) => !requiresScript);
581
+ if (!matchingScript) {
582
+ const alternatives = candidates.map(({ tag }) => tag).join(', ');
583
+ if (!parsed.script) {
584
+ throw new Error(`language "${parsed.lang}" must identify a supported script. Use one of: ${alternatives}.`);
585
+ }
586
+ throw new Error(`language "${parsed.lang}" uses an unsupported script. Use one of: ${alternatives}.`);
587
+ }
588
+
589
+ return Object.freeze({
590
+ lang: parsed.lang,
591
+ formattingLocale: parsed.lang,
592
+ language: matchingScript.language,
593
+ script: matchingScript.script,
594
+ labels: matchingScript.labels,
595
+ pagefindTranslations: matchingScript.pagefindTranslations,
596
+ });
597
+ };
@@ -0,0 +1,78 @@
1
+ export default Object.freeze({
2
+ tableSortAscending: 'Artan sıra ilə çeşidlə',
3
+ tableSortDescending: 'Azalan sıra ilə çeşidlə',
4
+ tableSortReset: 'İlkin sıranı bərpa et',
5
+ breadcrumb: 'Naviqasiya yolu',
6
+ appearance: 'Görünüş',
7
+ appearanceDark: 'Tünd',
8
+ appearanceLight: 'Açıq',
9
+ appearanceSystem: 'Sistem',
10
+ codeCopied: 'Kopyalandı',
11
+ codeCopyFailed: 'Kodu kopyalamaq mümkün olmadı',
12
+ copyCode: 'Kodu kopyala',
13
+ calloutCaution: 'Diqqət',
14
+ calloutDanger: 'Təhlükə',
15
+ calloutImportant: 'Vacib',
16
+ calloutNote: 'Qeyd',
17
+ calloutTip: 'Məsləhət',
18
+ calloutWarning: 'Xəbərdarlıq',
19
+ closeNavigation: 'Naviqasiyanı bağla',
20
+ editSource: 'Bu səhifəni redaktə et',
21
+ displaySettings: 'Görünüş',
22
+ focusReading: 'Fokuslanmış oxu',
23
+ footnoteBackReference: '{reference} istinadına qayıt',
24
+ footnotes: 'Haşiyələr',
25
+ readingWidth: 'Oxu eni',
26
+ readingWidthNarrow: 'Dar',
27
+ readingWidthStandard: 'Standart',
28
+ readingWidthWide: 'Geniş',
29
+ resetDisplaySettings: 'Sıfırla',
30
+ dismissBanner: 'Bildirişi bağla',
31
+ built: 'Yaradılıb',
32
+ images: 'Şəkillər',
33
+ imageCarousel: 'şəkil karuseli',
34
+ imageInspection: 'Şəkilə baxış',
35
+ imageInspectionActualSize: 'Həqiqi ölçünü göstər',
36
+ imageInspectionClose: 'Şəkil baxışını bağla',
37
+ imageInspectionFit: 'Şəkli pəncərəyə uyğunlaşdır',
38
+ inspectImage: 'Şəkilə bax: {description}',
39
+ navigationCollapsedAll: 'Bütün naviqasiya elementləri yığıldı.',
40
+ navigationCollapseAll: 'Hamısını yığ',
41
+ navigationControls: 'Naviqasiya ağacı idarələri',
42
+ navigationChildren: 'Alt səhifələr',
43
+ navigationExpandedAll: 'Bütün naviqasiya elementləri açıldı.',
44
+ navigationExpandAll: 'Hamısını aç',
45
+ navigationFilter: 'Səhifələri və qrupları süzgəcdən keçir',
46
+ navigationFilterEmpty: 'Uyğun səhifə və ya qrup yoxdur. Cari səhifə əlçatan olaraq qalır.',
47
+ navigationFilterMatches: 'Uyğun naviqasiya elementləri: {count}',
48
+ navigationLocatedCurrent: 'Cari səhifə tapıldı.',
49
+ navigationLocateCurrent: 'Cari səhifəni tap',
50
+ navigationMenu: 'Menyu',
51
+ nextImage: 'Növbəti şəkil',
52
+ nextPage: 'Növbəti səhifə',
53
+ note: 'Qeyd',
54
+ openInVsCode: 'VS Code-da aç',
55
+ notFound: 'Səhifə tapılmadı',
56
+ notFoundText: 'Tələb olunan səhifə mövcud deyil və ya köçürülmüş ola bilər.',
57
+ pageMoved: 'Səhifə köçürülüb',
58
+ pageMovedText: 'Bu ünvan indi bu səhifəni göstərir:',
59
+ pageNavigation: 'Səhifə məzmunu',
60
+ pageSequence: 'Səhifə ardıcıllığı',
61
+ pageSections: 'Bölmələr',
62
+ previousImage: 'Əvvəlki şəkil',
63
+ previousPage: 'Əvvəlki səhifə',
64
+ returnHome: 'Ana səhifəyə keç',
65
+ searchReturn: '{page} səhifəsinə qayıt',
66
+ search: 'Axtarış',
67
+ searchDescription: 'Bu saytın dərc edilmiş məzmununda axtarış edin.',
68
+ searchLoading: 'Axtarış yüklənir…',
69
+ searchNoScript: 'Axtarış JavaScript tələb edir. JavaScript mövcud olmadıqda səhifə naviqasiyasından istifadə edin.',
70
+ searchUnavailable: 'Axtarış mövcud deyil. Yerli iş zamanı axtarış indeksini yaratmaq və ya yeniləmək üçün norna build:local əmrini işlədin.',
71
+ siteBanners: 'Sayt bildirişləri',
72
+ siteNavigation: 'Səhifələr',
73
+ skipToContent: 'Məzmuna keç',
74
+ tableColumns: 'Cədvəl sütunları',
75
+ tableNextColumns: 'Növbəti sütunları göstər',
76
+ tableOverflowDescription: 'Daha çox cədvəl sütunu üfüqi olaraq mövcuddur.',
77
+ tablePreviousColumns: 'Əvvəlki sütunları göstər',
78
+ });