diff --git a/frontend/.typesafe-i18n.json b/frontend/.typesafe-i18n.json new file mode 100644 index 0000000..12eb8a1 --- /dev/null +++ b/frontend/.typesafe-i18n.json @@ -0,0 +1,7 @@ +{ + "baseLocale": "cs", + "adapter": "svelte", + "esmImports": true, + "outputPath": "./src/translations", + "$schema": "https://unpkg.com/typesafe-i18n@5.20.0/schema/typesafe-i18n.json" +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 2c1e416..f10de62 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,8 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "prettier --plugin-search-dir . --check . && eslint .", - "format": "prettier --plugin-search-dir . --write ." + "format": "prettier --plugin-search-dir . --write .", + "typesafe-i18n": "typesafe-i18n" }, "devDependencies": { "@playwright/test": "^1.29.2", @@ -46,6 +47,7 @@ "svelte-tippy": "^1.3.2", "swiper": "^8.4.6", "tippy.js": "^6.3.7", + "typesafe-i18n": "^5.20.0", "yup": "^0.32.11" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d76650e..774c62b 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -34,6 +34,9 @@ dependencies: tippy.js: specifier: ^6.3.7 version: 6.3.7 + typesafe-i18n: + specifier: ^5.20.0 + version: 5.20.0(typescript@4.9.4) yup: specifier: ^0.32.11 version: 0.32.11 @@ -2385,11 +2388,19 @@ packages: engines: {node: '>=10'} dev: true + /typesafe-i18n@5.20.0(typescript@4.9.4): + resolution: {integrity: sha512-uOvKnVkp1tXRDNBz9Aom54qs0LP2xWrtDliMPdKm9Scsnvn0DC7ZqjSGdOWxVplpbFbqYWNZuzx5Q5jWOjnBTA==} + hasBin: true + peerDependencies: + typescript: '>=3.5.1' + dependencies: + typescript: 4.9.4 + dev: false + /typescript@4.9.4: resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /unconfig@0.2.2: resolution: {integrity: sha512-JN1MeYJ/POnjBj7NgOJJxPp6+NcD6Nd0hEuK0D89kjm9GvQQUq8HeE2Eb7PZgtu+64mWkDiqeJn1IZoLH7htPg==} diff --git a/frontend/src/translations/cs/index.ts b/frontend/src/translations/cs/index.ts new file mode 100644 index 0000000..71ee449 --- /dev/null +++ b/frontend/src/translations/cs/index.ts @@ -0,0 +1,8 @@ +import type { BaseTranslation } from '../i18n-types.js' + +const cs: BaseTranslation = { + // TODO: your translations go here + HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n', +} + +export default cs diff --git a/frontend/src/translations/de/index.ts b/frontend/src/translations/de/index.ts new file mode 100644 index 0000000..eaae244 --- /dev/null +++ b/frontend/src/translations/de/index.ts @@ -0,0 +1,8 @@ +import type { Translation } from '../i18n-types.js' + +const de: Translation = { + // this is an example Translation, just rename or delete this folder if you want + HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n', +} + +export default de diff --git a/frontend/src/translations/formatters.ts b/frontend/src/translations/formatters.ts new file mode 100644 index 0000000..9e0741e --- /dev/null +++ b/frontend/src/translations/formatters.ts @@ -0,0 +1,11 @@ +import type { FormattersInitializer } from 'typesafe-i18n' +import type { Locales, Formatters } from './i18n-types.js' + +export const initFormatters: FormattersInitializer = (locale: Locales) => { + + const formatters: Formatters = { + // add your formatter functions here + } + + return formatters +} diff --git a/frontend/src/translations/i18n-svelte.ts b/frontend/src/translations/i18n-svelte.ts new file mode 100644 index 0000000..23d478d --- /dev/null +++ b/frontend/src/translations/i18n-svelte.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initI18nSvelte } from 'typesafe-i18n/svelte' +import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types.js' +import { loadedFormatters, loadedLocales } from './i18n-util.js' + +const { locale, LL, setLocale } = initI18nSvelte(loadedLocales, loadedFormatters) + +export { locale, LL, setLocale } + +export default LL diff --git a/frontend/src/translations/i18n-types.ts b/frontend/src/translations/i18n-types.ts new file mode 100644 index 0000000..9ae0378 --- /dev/null +++ b/frontend/src/translations/i18n-types.ts @@ -0,0 +1,19 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ +import type { BaseTranslation as BaseTranslationType } from 'typesafe-i18n' + +export type BaseTranslation = BaseTranslationType +export type BaseLocale = 'cs' + +export type Locales = + | 'cs' + +export type Translation = RootTranslation + +export type Translations = RootTranslation + +type RootTranslation = {} + +export type TranslationFunctions = {} + +export type Formatters = {} diff --git a/frontend/src/translations/i18n-util.async.ts b/frontend/src/translations/i18n-util.async.ts new file mode 100644 index 0000000..5d4505a --- /dev/null +++ b/frontend/src/translations/i18n-util.async.ts @@ -0,0 +1,25 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initFormatters } from './formatters.js' +import type { Locales, Translations } from './i18n-types.js' +import { loadedFormatters, loadedLocales, locales } from './i18n-util.js' + +const localeTranslationLoaders = { +} + +const updateDictionary = (locale: Locales, dictionary: Partial): Translations => + loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary } + +export const importLocaleAsync = async (locale: Locales): Promise => + (await localeTranslationLoaders[locale]()).default as unknown as Translations + +export const loadLocaleAsync = async (locale: Locales): Promise => { + updateDictionary(locale, await importLocaleAsync(locale)) + loadFormatters(locale) +} + +export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync)) + +export const loadFormatters = (locale: Locales): void => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/frontend/src/translations/i18n-util.sync.ts b/frontend/src/translations/i18n-util.sync.ts new file mode 100644 index 0000000..523c1eb --- /dev/null +++ b/frontend/src/translations/i18n-util.sync.ts @@ -0,0 +1,21 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initFormatters } from './formatters.js' +import type { Locales, Translations } from './i18n-types.js' +import { loadedFormatters, loadedLocales, locales } from './i18n-util.js' + +const localeTranslations = { +} + +export const loadLocale = (locale: Locales): void => { + if (loadedLocales[locale]) return + + loadedLocales[locale] = localeTranslations[locale] as unknown as Translations + loadFormatters(locale) +} + +export const loadAllLocales = (): void => locales.forEach(loadLocale) + +export const loadFormatters = (locale: Locales): void => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/frontend/src/translations/i18n-util.ts b/frontend/src/translations/i18n-util.ts new file mode 100644 index 0000000..109c3be --- /dev/null +++ b/frontend/src/translations/i18n-util.ts @@ -0,0 +1,33 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n' +import type { LocaleDetector } from 'typesafe-i18n/detectors' +import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n' +import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors' +import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types.js' + +export const baseLocale: Locales = 'cs' + +export const locales: Locales[] = [ +] + +export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales) + +export const loadedLocales: Record = {} as Record + +export const loadedFormatters: Record = {} as Record + +export const i18nString = (locale: Locales): TranslateByString => initI18nString(locale, loadedFormatters[locale]) + +export const i18nObject = (locale: Locales): TranslationFunctions => + initI18nObject( + locale, + loadedLocales[locale], + loadedFormatters[locale] + ) + +export const i18n = (): LocaleTranslationFunctions => + initI18n(loadedLocales, loadedFormatters) + +export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn(baseLocale, locales, ...detectors)