« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things')
-rw-r--r--src/data/things/language.js73
1 files changed, 57 insertions, 16 deletions
diff --git a/src/data/things/language.js b/src/data/things/language.js
index 1fa72def..afda258c 100644
--- a/src/data/things/language.js
+++ b/src/data/things/language.js
@@ -4,7 +4,7 @@ import {withAggregate} from '#aggregate';
 import {input} from '#composite';
 import * as html from '#html';
 import {accumulateSum, empty, withEntries} from '#sugar';
-import {isLanguageCode} from '#validators';
+import {isLanguageCode, isObject} from '#validators';
 import Thing from '#thing';
 import {languageOptionRegex} from '#wiki-data';
 
@@ -20,8 +20,6 @@ import {exitWithoutDependency, exposeConstant, exposeDependency}
   from '#composite/control-flow';
 import {flag, name} from '#composite/wiki-properties';
 
-import {withStrings} from '#composite/things/language';
-
 export class Language extends Thing {
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
@@ -63,15 +61,60 @@ export class Language extends Thing {
     // Mapping of translation keys to values (strings). Generally, don't
     // access this object directly - use methods instead.
     strings: [
-      withStrings({
-        from: input.updateValue({
-          validate: t => typeof t === 'object',
-        }),
-      }),
+      {
+        dependencies: [
+          input.updateValue({validate: isObject}),
+          'inheritedStrings',
+        ],
+
+        compute: (continuation, {
+          [input.updateValue()]: strings,
+          ['inheritedStrings']: inheritedStrings,
+        }) =>
+          (strings && inheritedStrings
+            ? continuation()
+            : strings ?? inheritedStrings),
+      },
 
-      exposeDependency({
-        dependency: '#strings',
-      }),
+      {
+        dependencies: ['inheritedStrings', 'code'],
+        transform(strings, {inheritedStrings, code}) {
+          const validStrings = {
+            ...inheritedStrings,
+            ...strings,
+          };
+
+          const optionsFromTemplate = template =>
+            Array.from(template.matchAll(languageOptionRegex))
+              .map(({groups}) => groups.name);
+
+          for (const [key, providedTemplate] of Object.entries(strings)) {
+            const inheritedTemplate = inheritedStrings[key];
+            if (!inheritedTemplate) continue;
+
+            const providedOptions = optionsFromTemplate(providedTemplate);
+            const inheritedOptions = optionsFromTemplate(inheritedTemplate);
+
+            const missingOptionNames =
+              inheritedOptions.filter(name => !providedOptions.includes(name));
+
+            const misplacedOptionNames =
+              providedOptions.filter(name => !inheritedOptions.includes(name));
+
+            if (!empty(missingOptionNames) || !empty(misplacedOptionNames)) {
+              logWarn`Not using ${code ?? '(no code)'} string ${key}:`;
+              if (!empty(missingOptionNames))
+                logWarn`- Missing options: ${missingOptionNames.join(', ')}`;
+              if (!empty(misplacedOptionNames))
+                logWarn`- Unexpected options: ${misplacedOptionNames.join(', ')}`;
+
+              validStrings[key] = inheritedStrings[key];
+            }
+          }
+
+          return validStrings;
+        },
+      },
     ],
 
     // May be provided to specify "default" strings, generally (but not
@@ -124,15 +167,13 @@ export class Language extends Thing {
 
     // TODO: This currently isn't used. Is it still needed?
     strings_htmlEscaped: [
-      withStrings(),
-
       exitWithoutDependency({
-        dependency: '#strings',
+        dependency: 'strings',
       }),
 
       {
-        dependencies: ['#strings'],
-        compute: ({'#strings': strings}) =>
+        dependencies: ['strings'],
+        compute: ({strings}) =>
           withEntries(strings, entries => entries
             .map(([key, value]) => [key, html.escape(value)])),
       },