Skip to content

Commit

Permalink
Add Translation component T
Browse files Browse the repository at this point in the history
  • Loading branch information
LorisSigrist committed Oct 12, 2023
1 parent b42d563 commit e4ad459
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 62 deletions.
64 changes: 37 additions & 27 deletions src/core/codegen/dts.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function generateDTS(localeDictionaries) {

const dts = new DTSBuilder();
dts.setDisclaimer(
"FILE AUTOGENERATED BY t18s\nYou can safely add this to your .gitignore",
"FILE AUTOGENERATED BY t18s\nYou can safely add this to your .gitignore"
);

dts.addModule(VIRTUAL_MODULE_PREFIX, (module) => {
Expand All @@ -20,7 +20,7 @@ export function generateDTS(localeDictionaries) {

module.addStatement(
`export type Locale = ${stringTypeUnion(locales)};`,
(s) => s.setDescription("The known locales"),
(s) => s.setDescription("The known locales")
);

module.addStatement(
Expand All @@ -29,32 +29,32 @@ export function generateDTS(localeDictionaries) {
.join(",")}]>;`,
(s) =>
s.setDescription(
"A store containing the available locales.\n\nThis store will only ever change during development, it is constant in production.",
),
"A store containing the available locales.\n\nThis store will only ever change during development, it is constant in production."
)
);

module.addStatement(`export const locale: Writable<Locale>;`, (s) =>
s.setDescription("The current locale"),
s.setDescription("The current locale")
);

module.addStatement(
"export function setLocale(newLocale: Locale): void;",
(s) =>
s.setDescription(
"Set the current locale. Equivalent to `locale.set(newLocale)`\n@param newLocale The new locale",
),
"Set the current locale. Equivalent to `locale.set(newLocale)`\n@param newLocale The new locale"
)
);

module.addStatement("export const isLoading: Readable<boolean>;", (s) =>
s.setDescription("If the current locale is still being loaded."),
s.setDescription("If the current locale is still being loaded.")
);

module.addStatement(
"export function init(options: { initialLocale: Locale, fallbackLocale?: Locale, loadingDelay?: number }) : Promise<void>",
(s) =>
s.setDescription(
"Initialize t18s.\nThis must be called before any other t18s function.",
),
"Initialize t18s.\nThis must be called before any other t18s function."
)
);

module.addStatement(
Expand All @@ -66,28 +66,16 @@ export function generateDTS(localeDictionaries) {
"This can be used to anticipate a locale change.",
"",
"Maybe preload the locale of the user's browser, since they're likely to switch to that.",
].join("\n"),
),
].join("\n")
)
);

module.addStatement(
"export const isLocale: (maybeLocale: unknown) => maybeLocale is Locale;",
(s) =>
s.setDescription(
"Convenience function to check if something is a valid locale.",
),
);

module.addStatement(
"export const t : Writable<<Key extends keyof Messages>(key: Key, ...values: (Messages[Key] extends undefined ? [(undefined | {})?] : [Messages[Key]])) => string>;",
(s) =>
s.setDescription(
[
"The translation function.",
"@param key A translation key.",
"@param values Any values that are interpolated into the translation.",
].join("\n"),
),
"Convenience function to check if something is a valid locale."
)
);

let messagesType = "export type Messages = {\n";
Expand All @@ -101,8 +89,30 @@ export function generateDTS(localeDictionaries) {
messagesType += "};";

module.addStatement(messagesType, (s) =>
s.setDescription("Available Translations and their Arguments"),
s.setDescription("Available Translations and their Arguments")
);

// t Store
module.addStatement(
"export const t : Writable<<Key extends keyof Messages>(key: Key, ...values: (Messages[Key] extends undefined ? [(undefined | {})?] : [Messages[Key]])) => string>;",
(s) =>
s.setDescription(
[
"The translation store.",
"@param key A translation key.",
"@param values Any values that are interpolated into the translation.",
].join("\n")
)
);


// T Component
module.addImport(
"import type { SvelteComponentTyped } from 'svelte';"
);
module.addStatement("export class T<Key extends keyof Messages> extends SvelteComponentTyped<Messages[Key] extends undefined ? { key: Key } : { key: Key, values: Messages[Key] }, {}, {}> { };", s => {
s.setDescription("The t18s translation component.")
});
});

return dts.build();
Expand Down
1 change: 1 addition & 0 deletions src/core/codegen/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function generateMainModuleCode(localesIterable, verbose) {

return `
import { writable, get } from 'svelte/store';
export { T } from "$t18s/runtime";
const messages = {}
Expand Down
24 changes: 0 additions & 24 deletions src/core/codegen/utils/client-side-console.js

This file was deleted.

1 change: 1 addition & 0 deletions src/core/codegen/utils/dtsBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Module {
*/
addStatement(code, statementBuilder = undefined) {
const statement = new Statement(code);

if (statementBuilder) {
statementBuilder(statement);
}
Expand Down
10 changes: 8 additions & 2 deletions src/core/codegen/utils/stringUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ export const addParentheses = (str) => `(${str})`;

/**
* Returns a type union of the given strings
* @param {string[]} strings ["a","b","c"]
* @param {Iterable<string>} strings The strings to union (e.g. ["a", "b", "c"])
* @returns "a" | "b" | "c"
*/
export const stringTypeUnion = (strings) => strings.map(addQuotes).join("|");
export const stringTypeUnion = (strings) => {
let union = "";
for (const str of strings) {
union += `"${str}"|`;
}
return union.slice(0, -1);
}

/**
* Indents some text by one level.
Expand Down
27 changes: 22 additions & 5 deletions src/core/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { basename, resolve } from "path";
import { readdir, writeFile } from "fs/promises";
import { basename, dirname, resolve } from "node:path";
import { readdir, writeFile } from "node:fs/promises";
import { YamlHandler } from "./file-handling/formats/yaml.js";
import { JsonHandler } from "./file-handling/formats/json.js";
import { Logger } from "./utils/logger.js";
Expand All @@ -17,6 +17,8 @@ import { Reporter } from "./utils/reporter.js";
import { ResultMatcher } from "./utils/resultMatcher.js";
import { buffer } from "./utils/bufferPromise.js";
import { LocaleRegistry, LocaleNotFoundException } from "./localeRegistry.js";
import { normalizePath } from "vite";
import { fileURLToPath } from "node:url";

/**
* TypeSafe translations for Svelte & SvelteKit.
Expand All @@ -40,16 +42,16 @@ export function t18sCore(pluginConfig) {

registry.addEventListener("changed", async () => await regenerateDTS());
registry.addEventListener("locale_added", (e) => {
triggerHMREvent("t18s:createLocale", e.detail.locale);
reporter.localeCreated(e.detail.locale);
triggerHMREvent("t18s:createLocale", e.detail.locale);
});
registry.addEventListener("locale_removed", (e) => {
triggerHMREvent("t18s:removeLocale", e.detail.locale);
reporter.localeDeleted(e.detail.locale);
triggerHMREvent("t18s:removeLocale", e.detail.locale);
});
registry.addEventListener("locale_updated", (e) => {
triggerHMREvent("t18s:invalidateLocale", e.detail.locale);
reporter.localeUpdated(e.detail.locale);
triggerHMREvent("t18s:invalidateLocale", e.detail.locale);
});

/** Handles interactions with translation files */
Expand Down Expand Up @@ -240,6 +242,12 @@ export function t18sCore(pluginConfig) {
id = cleanUrl(id);
if (!id.startsWith(RESOLVED_VIRTUAL_MODULE_PREFIX)) return;

//Runtime entry
if (id === RESOLVED_VIRTUAL_MODULE_PREFIX + "/runtime") {
const runtimeEntryPath = getRuntimeEntryPath();
return `export * from '${runtimeEntryPath}';`;
}

if (id === RESOLVED_VIRTUAL_MODULE_PREFIX) {
const locales = registry.getLocales();
return generateMainModuleCode(locales, config.verbose);
Expand Down Expand Up @@ -287,6 +295,15 @@ export function t18sCore(pluginConfig) {
};
}

function getRuntimeEntryPath() {
const thisModulePath = normalizePath(dirname(fileURLToPath(import.meta.url)));
return thisModulePath.replace(
/\/t18s\/src\/core$/,
"/t18s/src/core/runtime/index.js",
);
}


/**
* Remove hash and query parameters from a url.
* @param {string} url
Expand Down
16 changes: 16 additions & 0 deletions src/core/runtime/T.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script context="module">
</script>

<script>
//@ts-ignore
import { t } from "$t18s";
/** @type {string} */
export let key;
/** @type {any} */
export let values = undefined;
</script>

{$t(key, values)}
1 change: 1 addition & 0 deletions src/core/runtime/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as T } from "./T.svelte";
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export function t18s(userConfig = {}) {
/** @type {import("./types.js").t18sFullConfig} */
const pluginConfig = { ...DEFAULT_CONFIG, ...userConfig };

return [t18sCore(pluginConfig), t18sToolkit()];
return [t18sToolkit(), t18sCore(pluginConfig)];
}
6 changes: 3 additions & 3 deletions src/toolkit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ export function t18sToolkit() {

return {
name: "t18s-toolkit",
apply: "serve", // only apply to dev server
//apply: "serve", // only apply to dev server
enforce: "pre",

configResolved(resolvedConfig) {
config = resolvedConfig;
},

resolveId(id, _, options) {
if (options?.ssr) return;
//if (options?.ssr) return;

if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {
return id.replace(VIRTUAL_MODULE_PREFIX, toolkitPath);
}
},

async load(id, options) {
if (options?.ssr) return;
//if (options?.ssr) return;
if (id.startsWith(toolkitPath)) {
// read file ourselves to avoid getting shut out by vites fs.allow check
const file = cleanUrl(id);
Expand Down

0 comments on commit e4ad459

Please sign in to comment.