Skip to content

Commit

Permalink
Merge pull request DouglasNeuroInformatics#913 from joshunrau/main
Browse files Browse the repository at this point in the history
  • Loading branch information
joshunrau authored Aug 9, 2024
2 parents bbd5b31 + 0fda6f2 commit 52e21d2
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 111 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* eslint-disable perfectionist/sort-objects */

import { translations } from './translations.ts';

const { defineInstrument } = await import('/runtime/v1/opendatacapture@1.0.0/core.js');
const { i18n } = await import('/runtime/v1/opendatacapture@1.0.0/unstable/i18n.js');
const { z } = await import('/runtime/v1/zod@3.23.6/index.js');

i18n.init();

export default defineInstrument({
kind: 'FORM',
language: ['en', 'fr'],
internal: {
edition: 1,
name: 'FAVORITE_COLOR'
},
tags: {
en: ['Dynamic'],
fr: ['Dynamique']
},
content: {
hasFavoriteColor: {
kind: 'boolean',
label: {
en: 'Do you have a favorite color?',
fr: 'Avez-vous une couleur préférée ?'
},
variant: 'radio'
},
favoriteColor: {
kind: 'dynamic',
deps: ['hasFavoriteColor'],
render(data) {
if (!data?.hasFavoriteColor) {
return null;
}
return {
kind: 'string',
label: {
en: 'Favorite Color',
fr: 'Couleur préférée'
},
options: {
en: {
red: 'Red',
green: 'Green',
blue: 'Blue'
},
fr: {
red: 'Rouge',
green: 'Vert',
blue: 'Bleu'
}
},
variant: 'select'
};
}
}
},
details: {
description: {
en: 'This is an example of a simple form with conditional rendering and validation logic',
fr: 'Voici un exemple de formulaire simple avec un rendu conditionnel et une logique de validation'
},
estimatedDuration: 1,
instructions: {
en: ['Please respond to all questions'],
fr: ['Veuillez répondre à toutes les questions']
},
license: 'Apache-2.0',
title: {
en: 'Favorite Color',
fr: 'Couleur préférée'
}
},
measures: {},
validationSchema: z
.object({
hasFavoriteColor: z.boolean({ message: translations.requiredField[i18n.resolvedLanguage] }),
favoriteColor: z.enum(['red', 'blue', 'green']).optional()
})
.superRefine((data, ctx) => {
if (data.hasFavoriteColor && !data.favoriteColor) {
ctx.addIssue({
code: 'custom',
path: ['favoriteColor'],
message: translations.requiredField[i18n.resolvedLanguage]
});
}
})
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const translations = {
requiredField: {
en: 'This field is required',
fr: 'Ce champ est obligatoire'
}
};
6 changes: 6 additions & 0 deletions packages/i18next/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ import core from './translations/core.json';
i18n.setDefaultNamespace('core');
i18n.addPreInitTranslations({ core });

i18n.on('languageChanged', (lang) => {
if (typeof window !== 'undefined') {
document.documentElement.lang = lang;
}
});

export { i18n };
4 changes: 2 additions & 2 deletions packages/i18next/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../../jsconfig.json",
"compilerOptions": {
"resolveJsonModule": true
"lib": ["ESNext", "DOM", "DOM.Iterable"]
},
"extends": "../../jsconfig.json",
"include": ["src/**/*", "*.js", "*.cjs"]
}
57 changes: 57 additions & 0 deletions runtime/v1/src/opendatacapture@1.0.0/unstable/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Language } from '@opendatacapture/schemas/core';

const documentElement = window.top!.document.documentElement;
const fallbackLanguage: Language = 'en';

type LanguageChangeHandler = (this: void, language: Language) => void;

function extractLanguageProperty(element: HTMLElement): Language {
if (element.lang === 'en' || element.lang === 'fr') {
return element.lang;
}
console.error(`Unexpected value for HTMLElement 'lang' attribute: '${element.lang}'`);
return fallbackLanguage;
}

class I18N {
#handleLanguageChange: LanguageChangeHandler | null;
#isInitialized: boolean;
#languageAttributeObserver: MutationObserver;
#resolvedLanguage: Language | null;

constructor() {
this.#isInitialized = false;
this.#handleLanguageChange = null;
this.#resolvedLanguage = null;
this.#languageAttributeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'lang') {
this.#resolvedLanguage = extractLanguageProperty(mutation.target as HTMLElement);
this.#handleLanguageChange?.(this.resolvedLanguage);
}
});
});
}

get resolvedLanguage() {
if (!this.#isInitialized) {
throw new Error('Cannot access property before init method is called');
}
return this.#resolvedLanguage!;
}

init() {
this.#isInitialized = true;
this.#resolvedLanguage = extractLanguageProperty(documentElement);
this.#languageAttributeObserver.observe(documentElement, { attributes: true });
}

onLanguageChange(handler: LanguageChangeHandler) {
if (!this.#isInitialized) {
throw new Error('Cannot register handler before init method is called');
}
this.#handleLanguageChange = handler;
}
}

export const i18n = new I18N();

0 comments on commit 52e21d2

Please sign in to comment.