import type { GetStaticProps, NextPage } from "next"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; import { Fragment } from "react"; import { useEffect } from "react"; import { Children } from "react"; import { ChangeEvent, ComponentType, FunctionComponent, ReactNode, useState } from "react"; import { T, tBind, TextPropType, Translate, useTranslation, TDictionaryRoot, TFatString, TString } from "../dist/esm"; import { Links } from "../lib/links"; import styles from "../styles/Home.module.css"; interface PageProps { greeting: TFatString; message: TFatString; info: TFatString; nested: TFatString; } // Mock service data export const getStaticProps: GetStaticProps = async context => { return { props: { greeting: { en: "Hello", fr: "Bonjour", de: "Hallo" }, message: { en: "Let's translate text!", fr: "Traduisons le texte!", de: "Lassen Sie uns Text übersetzen!", cy: "Gadewch i ni gyfieithu testun!" }, info: { // Demo substitution en: 'The word for 1 is "%1" and the word for 2 is "%2"', fr: 'Le mot pour 2 est "%2" et le mot pour 1 est "%1".' }, nested: { "en": "Here's a %1[useful link] and here's some %2[italic text]", "en-GB": "Would you care for a %1[useful link]? Or maybe some %2[italic text]?", "fr": "Voici %2[du texte en italique] et un %1[lien utile]", "de": "Hier ist ein %1[nützlicher Link] und hier ein %2[kursiver Text]" } } }; }; // Translation dictionary const dictionary: TDictionaryRoot = { $$dict: { "site": { "*": "Interminimal", "fr": "Chose internationale" }, // Numbers "one": { en: "One", fr: "Un", de: "Ein", cy: "Un" }, "two": { en: "Two", fr: "Deux", de: "Zwei", cy: "Dau" }, "three": { en: "Three" }, // Language names "en": { en: "English", fr: "Anglais" }, "en-GB": { "en-GB": "British" }, "fr": { fr: "Français" }, "de": { en: "German", fr: "Allemand", de: "Deutsch" }, "cy": { en: "Welsh", cy: "Cymraeg" }, "cat": { en: "cat", de: "Katze", cy: "cath" }, "colour": { "en-GB": "colour", "en": "color" }, // Plurals "cats": { en: { one: "%1 cat", other: "%1 cats" }, de: { one: "%1 Katze", other: "%1 Katzen" }, cy: { zero: "%1 cathod", one: "%1 gath", two: "%1 gath", few: "%1 cath", many: "%1 chath", other: "%1 cath" } }, // Silly deep nesting. "silly": { en: "Top level %1[Level one %1[Level two] and " + "%2[also level two with %1[level three]]]", fr: "Niveau supérieur %1[Niveau un %1[Niveau deux]" + " et %2[aussi niveau deux avec %1[niveau trois]]]", de: "Oberste Ebene %1[Ebene eins %1[Ebene zwei] " + "und %2[auch Ebene zwei mit %1[Ebene drei]]]", cy: "Lefel uchaf %1[Lefel un %1[Lefel dau] a " + "%2[hefyd lefel dau gyda %1[lefel tri]]]" }, // A nested dictionary for use with dictionaryFromTag "madness": { $$dict: { site: { en: "Or maybe something else", fr: "Ou peut-être autre chose" } } }, "h.siteName": { en: "It's Called", fr: "C'est Appelé" }, "h.someCats": { en: "Some Cats", fr: "Quelques Chats", cy: "Rhai Cathod" } } }; // Demo how to use Intl with Interminimal const DateFormat: ComponentType< { date: Date } & Intl.DateTimeFormatOptions > = ({ date, ...opt }) => { const ctx = useTranslation(); // Use our languages stack to find a format for our locale const dtf = new Intl.DateTimeFormat(ctx.languages, opt); // Find out which language was matched... const { locale } = dtf.resolvedOptions(); // Format the date and create a literal ts with the available // locale const ts = TString.literal(dtf.format(date), locale); // Format it using T return ; }; const TList: ComponentType<{ children: ReactNode; type?: "conjunction" | "disjunction"; style?: "long" | "short" | "narrow"; }> = ({ children, ...opt }) => { const ctx = useTranslation(); // @ts-ignore - no type mapping available const lf = new Intl.ListFormat(ctx.languages, opt); const { locale } = lf.resolvedOptions(); // Make the children into a list of args, %1, %2 etc const list = Array.from( { length: Children.count(children) }, (_v, i) => `%${i + 1}` ); // Format the list into a template string and make the translated // template and locale into a TString const ts = TString.literal(lf.format(list), locale); // Format it using T return {children}; }; const Clock: ComponentType = ({ ...opt }) => { const [now, setNow] = useState(null); useEffect(() => { const update = () => setNow(new Date()); const timer = setInterval(update, 500); update(); return () => clearInterval(timer); }, []); if (!now) return null; return ; }; const Box: ComponentType<{ children: ReactNode; lang?: string }> = ({ children }) => <>[{children}]; interface TTitleProps { text: TextPropType; lang?: string; } // Inject page title into a NextJS component. We have to do the // translation explicitly because we can't nest a T inside a Head // Use this component *outside* of any other const TTitle: ComponentType = ({ text, ...rest }) => { const { str, props } = useTranslation().translateTextAndProps(text, rest); return ( {str} ); }; interface LanguageState { lang: string; setLang: (lang: string) => void; label: string; } const useLanguageState = ( defaultLang: string, label: string ): LanguageState => { const [lang, setLang] = useState(defaultLang); return { lang, setLang, label }; }; const useLanguageStates = ( lang1: string, lang2: string, lang3: string ): LanguageState[] => { return [ useLanguageState(lang1, "one"), useLanguageState(lang2, "two"), useLanguageState(lang3, "three") ]; }; const langs = ["en-GB", "en", "fr", "de", "cy"]; const LanguagePicker: ComponentType<{ label: TextPropType; state: LanguageState; }> = ({ label, state }) => { // Bake an alternative to const Toption = tBind("option"); const onChange = (e: ChangeEvent) => { e.preventDefault(); state.setLang(e.target.value); }; return ( ); }; const Stack: ComponentType = () => { const ctx = useTranslation(); return (

{ctx.languages.map(lang => ( ))}

); }; const loader = ({ src, width }: { src: string; width: number }) => `${src}/${width}/${Math.round((width * 9) / 16)}`; const Block: ComponentType< PageProps & { lang: string; state: LanguageState[] } > = ({ greeting, message, info, nested, state }) => { const counts = [0, 1, 1.5, 2, 3, 6, 42]; // Bake an alternative to const Tli = tBind("li"); const Tdiv = tBind("div"); const Th2 = tBind("h2"); const Tp = tBind("p"); const TImage = tBind(Image); const TBox = tBind(Box); return (
s.lang)}> {state.map(s => ( {" "} ))}
    {/* inline fat string */}
{/* `info` has two placeholders which we fill with "one" and "two" */}
{/* translate alt attribute via cat tag */}
{/* many cats, many plurals */} {counts.map((n, i) => ( {String(n)} ))}
); }; const HomePage: NextPage = props => { const state1 = useLanguageStates("de", "en", "fr"); const state2 = useLanguageStates("en", "de", "fr"); const state3 = useLanguageStates("fr", "de", "en-GB"); return (

Interminimal Demo

); }; export default HomePage;