diff --git a/src/core/HMR.js b/src/core/HMR.js index c9ccb7c..87bd632 100644 --- a/src/core/HMR.js +++ b/src/core/HMR.js @@ -3,8 +3,6 @@ * "t18s:addDictionary": { locale: string, domain: string } * "t18s:removeDictionary": { locale: string, domain: string } * "t18s:reloadDictionary": { locale: string, domain: string } - * "t18s:addLocale": { locale: string } - * "t18s:removeLocale": { locale: string } * }} HMREventMap */ diff --git a/src/core/codegen/dts.js b/src/core/codegen/dts.js index 5dcba42..4d12529 100644 --- a/src/core/codegen/dts.js +++ b/src/core/codegen/dts.js @@ -8,7 +8,7 @@ import { MessageCatalogue } from "../MessageCatalogue.js"; * @param {MessageCatalogue} Catalogue */ export function generateDTS(config, Catalogue) { - const locales = Catalogue.getLocales(); + const locales = config.locales; const messagesTypeMap = generateMessagesTypeMap(config, Catalogue); const dts = new DTSBuilder(); @@ -29,7 +29,12 @@ export function generateDTS(config, Catalogue) { s.setDescription("The known locales"), ); - module.addStatement(`export const locales : Readable;`, (s) => + + module.addStatement(`export const fallbackLocale: Locale | undefined;`, (s) => + s.setDescription("The fallback locale that's currently in use.") + ); + + module.addStatement(`export const locales : Locales;`, (s) => s.setDescription( "A store containing the available locales.\n\nThis store will only ever change during development, it is constant in production.", ), @@ -106,15 +111,6 @@ export function generateDTS(config, Catalogue) { ].join("\n"), ), ); - - // T Component - module.addImport("import type { SvelteComponentTyped } from 'svelte';"); - module.addStatement( - "export class T extends SvelteComponentTyped { };", - (s) => { - s.setDescription("The t18s translation component."); - }, - ); }); return dts.build(); diff --git a/src/core/codegen/locale.js b/src/core/codegen/locale.js deleted file mode 100644 index c81796a..0000000 --- a/src/core/codegen/locale.js +++ /dev/null @@ -1,17 +0,0 @@ -import { MessageCatalogue } from "../MessageCatalogue.js"; - -/** - * @param {MessageCatalogue} Catalogue - * @returns {string} - */ -export function generateLocaleModule(Catalogue) { - const locales = [...Catalogue.getLocales()]; - - const code = ` - import { writable } from 'svelte/store'; -export const locales = writable(${JSON.stringify(locales)}); -`; - - console.log(code); - return code; -} diff --git a/src/core/codegen/main.js b/src/core/codegen/main.js index aec8fd2..4adf7f9 100644 --- a/src/core/codegen/main.js +++ b/src/core/codegen/main.js @@ -7,15 +7,11 @@ import { MessageCatalogue } from "../MessageCatalogue.js"; * @returns {string} */ export function generateMainModuleCode(config, Catalogue) { - const locales = [...Catalogue.getLocales()]; - const code = ` import { writable, get } from 'svelte/store'; - import { locales } from 't18s-internal:locales' import config from 't18s-internal:config'; - - - export { locales }; + + export const locales = config.locales; //Keeps track of the current catalogue of dictionaries. Double-Keyed by locale and domain const Catalogue = {} @@ -28,7 +24,7 @@ export function generateMainModuleCode(config, Catalogue) { //We need to explicitly list each import here to make sure none of //the dictionaries are accidentally removed by tree-shaking const loaders = { - ${locales.map((loc) => { + ${config.locales.map((loc) => { const domains = Catalogue.getDomains(loc); let code = `"${loc}" : {\n`; @@ -43,7 +39,7 @@ export function generateMainModuleCode(config, Catalogue) { //List of domains that should be loaded eagerly when a new locale is loaded const eagerlyLoadedDomains = new Set(["${config.defaultDomain}"]); - let fallbackLocale = undefined; + export let fallbackLocale = undefined; let loadingDelay = 200; export async function init(options) { @@ -96,7 +92,7 @@ export function generateMainModuleCode(config, Catalogue) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); export function isLocale(maybeLocale) { - return get(locales).includes(maybeLocale); + return locales.includes(maybeLocale); } async function loadLocale(newLocale) { @@ -116,7 +112,7 @@ export function generateMainModuleCode(config, Catalogue) { function parseKey(key) { const [first, second] = key.split(":"); if(!first) throw new Error("[t18s] Invalid key: " + key); - if(!second) return {domain: "${config.defaultDomain}", key: first}; + if(!second) return {domain: config.defaultDomain, key: first}; else return {domain: first, key: second}; } @@ -211,23 +207,8 @@ export function generateMainModuleCode(config, Catalogue) { t.set(getMessage); //update the store }); - - import.meta.hot.on("t18s:addLocale", async (data) => { - locales.update((locales) => [...locales, data.locale]); - }); - - import.meta.hot.on("t18s:removeLocale", async (data) => { - locales.update((locales) => locales.filter((l) => l !== data.locale)); - - console.log("REMOVING LOCALE", data.locale, get(locale), get(locales)); - - //Switch locale if the current locale was removed - if(data.locale === get(locale)) { - locale.set(get(locales)[0]); - t.set(getMessage); //rerender the component - } - }); } `; + return code; } diff --git a/src/core/index.js b/src/core/index.js index 52e5079..8d04bf0 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -26,8 +26,6 @@ import { cleanUrl } from "./utils/id.js"; import { existsSync } from "node:fs"; import { createHMRDispatcher } from "./HMR.js"; import { generateConfigModule } from "./codegen/config.js"; -import { generateLocaleModule } from "./codegen/locale.js"; -import { $ } from "kleur/colors"; /** * TypeSafe translations for Svelte & SvelteKit. @@ -55,15 +53,6 @@ export function t18sCore(pluginConfig) { /** Keeps track of the messages that exist & where to find them */ const Catalogue = new MessageCatalogue(); - - Catalogue.addEventListener("locale_removed", e => { - dispatch("t18s:removeLocale", { locale: e.detail.locale }); - }) - - Catalogue.addEventListener("locale_added", e => { - dispatch("t18s:addLocale", { locale: e.detail.locale }); - }); - Catalogue.addEventListener( "messages_changed", async () => await regenerateDTS() @@ -170,19 +159,28 @@ export function t18sCore(pluginConfig) { } /** - * Reads the initial translation files and generates the initial code. - * @param { import("./types.js").ResolvedPluginConfig} config + * Safely list the files that are in the given directory. If the reading fails, an empty array is returned & a warning is logged. + * @example ["file1.txt", "file2.txt"] + * + * @param {string} dir + * @returns {Promise} */ - async function loadInitialLocales(config) { - const readdirResult = await buffer(readdir(config.translationsDir)); - - const files = new ResultMatcher(readdirResult) + async function getFilesInDir(dir) { + const readdirResult = await buffer(readdir(dir)); + return new ResultMatcher(readdirResult) .catchAll((e) => { - logger.warn("Could not read translation directory\n" + e); + logger.warn("Could not read directory " + dir); return []; }) .run(); + } + /** + * Reads the initial translation files and generates the initial code. + * @param { import("./types.js").ResolvedPluginConfig} config + */ + async function loadInitialLocales(config) { + const files = await getFilesInDir(config.translationsDir); const paths = files.map((file) => resolve(config.translationsDir, file)); /** @param {string} path */ @@ -238,6 +236,7 @@ export function t18sCore(pluginConfig) { ), verbose: pluginConfig.verbose, defaultDomain: pluginConfig.defaultDomain, + locales: pluginConfig.locales, }; logger = new Logger(resolvedConfig, config.verbose); @@ -254,7 +253,6 @@ export function t18sCore(pluginConfig) { resolveMainModuleId, resolveRuntimeId, resolveConfigModuleId, - resolveLocaleModuleId, ]; for (const resolver of resolvers) { @@ -273,7 +271,6 @@ export function t18sCore(pluginConfig) { loadDictionaryModule, loadRuntimeModule, loadConfigModule, - loadLocaleModule ]; //Attempt to load the module from all loaders @@ -401,18 +398,6 @@ async function loadConfigModule(resolved_id, config, Catalogue) { return generateConfigModule(config); } -/** - * @param {string} resolved_id - * @param {import("./types.js").ResolvedPluginConfig} config - * - * @param {MessageCatalogue} Catalogue - * @returns {Promise} - */ -async function loadLocaleModule(resolved_id, config, Catalogue) { - if (resolved_id !== "\0t18s-internal:locales") return null; - return generateLocaleModule(Catalogue); -} - /** * If the unresolved_id is for the t18s-runtime, this function will resolve it. * @param {string} unresolved_id @@ -459,13 +444,3 @@ function resolveConfigModuleId(unresolved_id) { if (unresolved_id !== "t18s-internal:config") return null; return "\0t18s-internal:config"; } - -/** - * If the unresolved_id is for the t18s locales, this function will resolve it. - * @param {string} unresolved_id - * @returns {string | null} - */ -function resolveLocaleModuleId(unresolved_id) { - if (unresolved_id !== "t18s-internal:locales") return null; - return "\0t18s-internal:locales"; -} diff --git a/src/core/types.d.ts b/src/core/types.d.ts index 5d5860d..8351770 100644 --- a/src/core/types.d.ts +++ b/src/core/types.d.ts @@ -27,4 +27,5 @@ export interface ResolvedPluginConfig { translationsDir: string; verbose: boolean; defaultDomain: string; + locales: [string, ...string[]]; } diff --git a/src/index.js b/src/index.js index 8e13969..903c6dc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ import { t18sCore } from "./core/index.js"; import { t18sToolkit } from "./toolkit/index.js"; -/** @type {import("./types.js").t18sFullConfig} */ +/** @type {import("./types.js").t18sDefaultConfig} */ const DEFAULT_CONFIG = { translationsDir: "src/translations", dts: "src/$t18s.d.ts", @@ -14,7 +14,7 @@ const DEFAULT_CONFIG = { * @param {import("./types.js").t18sUserConfig} userConfig * @returns {import("vite").Plugin[]} */ -export function t18s(userConfig = {}) { +export function t18s(userConfig) { /** @type {import("./types.js").t18sFullConfig} */ const pluginConfig = { ...DEFAULT_CONFIG, ...userConfig }; diff --git a/src/types.d.ts b/src/types.d.ts index d1d728a..e14a2ed 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -25,6 +25,22 @@ export type t18sUserConfig = { * @default "messages" */ defaultDomain?: string; + + /** + * The locales that should be made available. + */ + locales: [string, ...string[]], }; +export type t18sDefaultConfig = FlipOptional; export type t18sFullConfig = Required; + + +type OptionalKeys = { + [K in keyof T]-?: {} extends Pick ? K : never +}[keyof T]; + +type FlipOptional = (Required>> & + Partial>>) extends infer O + ? { [K in keyof O]: O[K] } + : never; \ No newline at end of file