### Complete project settings example Source: https://inlang.com/docs/settings A comprehensive example of a project.inlang/settings.json file. ```json { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", "locales": ["en", "de", "fr", "es"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-t-function-matcher@3/dist/index.js" ], "plugin.inlang.i18next": { "pathPattern": { "common": "./locales/{locale}/common.json", "errors": "./locales/{locale}/errors.json" }, "variableReferencePattern": ["{{", "}}"] } } ``` -------------------------------- ### Example User Configuration Source: https://inlang.com/docs/plugin-api An example of how users would configure your plugin in their `settings.json` file, including base locale, locales, modules, and plugin-specific settings. ```json { "baseLocale": "en", "locales": ["en", "de"], "modules": ["./plugins/my-plugin.js"], "plugin.my.json": { "pathPattern": "./messages/{locale}.json", "sort": "asc" } } ``` -------------------------------- ### Install local plugin via npm Source: https://inlang.com/docs/install-plugin Install a plugin package locally using npm to reference it from the node_modules directory. ```bash npm install @inlang/plugin-i18next ``` -------------------------------- ### toBeImportedFiles Method Example Source: https://inlang.com/docs/plugin-api Example implementation of the `toBeImportedFiles` method, which discovers files to be imported. It returns an array of file descriptors, each specifying the path and locale. ```typescript toBeImportedFiles: async ({ settings }) => { return [ { path: "./messages/en.json", locale: "en" }, { path: "./messages/de.json", locale: "de" }, ]; } ``` -------------------------------- ### importFiles Method Example Source: https://inlang.com/docs/plugin-api Example signature for the `importFiles` method, which parses file content into inlang's data model. It expects an array of files and project settings, returning bundles, messages, and variants. ```typescript importFiles: async ({ files, settings }) => { return { bundles: [...], messages: [...], variants: [...], }; } ``` -------------------------------- ### Install multiple plugins Source: https://inlang.com/docs/install-plugin Include multiple URLs in the modules array to enable different file handling or functionality. ```json { "baseLocale": "en", "locales": ["en", "de"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-t-function-matcher@3/dist/index.js" ], "plugin.inlang.i18next": { "pathPattern": "./locales/{locale}.json" } } ``` -------------------------------- ### exportFiles Method Example Source: https://inlang.com/docs/plugin-api Example signature for the `exportFiles` method, which converts inlang's data model back into files for export. It takes bundles, messages, variants, and settings, returning an array of files to be written. ```typescript exportFiles: async ({ bundles, messages, variants, settings }) => { return [ { locale: "en", name: "en.json", content: new TextEncoder().encode(JSON.stringify(data)), }, ]; } ``` -------------------------------- ### Pattern Syntax Examples Source: https://inlang.com/docs/data-model Illustrates the syntax for patterns, including plain text, expressions with variables, and markup placeholders. ```plaintext {count} // plain variable {count: number} // format as number {date: date} // format as date {count: plural} // pluralization {#link}Open{/link} // markup wrapper {#icon/} // standalone markup ``` -------------------------------- ### Plugin version pinning examples Source: https://inlang.com/docs/install-plugin Specify versions in the URL to ensure reproducible builds and avoid breaking changes. ```text @inlang/plugin-i18next@3 # Major version (3.x.x) @inlang/plugin-i18next@3.5 # Minor version (3.5.x) @inlang/plugin-i18next@3.5.2 # Exact version ``` -------------------------------- ### Configure Plugin in settings.json Source: https://inlang.com/docs/write-plugin Example of how a user would configure the plugin in their `settings.json` file, specifying the `pathPattern` for translation files. ```json { "baseLocale": "en", "locales": ["en", "de", "fr"], "modules": ["./my-plugin.js"], "plugin.my.json": { "pathPattern": "./i18n/{locale}.json" } } ``` -------------------------------- ### Load Project and Query Bundles Source: https://inlang.com/docs/crud-api Loads a project from a directory and queries all bundles using the Kysely database instance. Ensure the '@inlang/sdk' package is installed and 'fs' is available. ```typescript import { loadProjectFromDirectory } from "@inlang/sdk"; const project = await loadProjectFromDirectory({ path: "./project.inlang", fs: fs, }); // project.db is a Kysely instance const bundles = await project.db .selectFrom("bundle") .selectAll() .execute(); ``` -------------------------------- ### Run Translation Check CLI Source: https://inlang.com/docs/write-tool Example command to execute the translation check CLI tool using tsx. ```bash $ npx tsx check-translations.ts Found 3 missing translations: - "greeting" is missing locale "fr" - "error_404" is missing locale "de" - "error_404" is missing locale "fr" ``` -------------------------------- ### Get Project Settings Source: https://inlang.com/docs/write-tool Retrieves the project settings, including the base locale and a list of all supported locales. ```typescript const settings = await project.settings.get(); console.log("Base locale:", settings.baseLocale); console.log("Locales:", settings.locales); // Base locale: en // Locales: ["en", "de", "fr"] ``` -------------------------------- ### Complete Inlang Plugin Implementation Source: https://inlang.com/docs/write-plugin A full example of an inlang plugin, including settings schema, file import logic, and file export logic for handling translation files. ```typescript import type { InlangPlugin } from "@inlang/sdk"; import { Type } from "@sinclair/typebox"; const PluginSettings = Type.Object({ pathPattern: Type.String({ description: "Path pattern for translation files", examples: ["./messages/{locale}.json"], }), }); export const plugin: InlangPlugin<{ "plugin.my.json": typeof PluginSettings; }> = { key: "plugin.my.json", settingsSchema: PluginSettings, toBeImportedFiles: async ({ settings }) => { const pattern = settings["plugin.my.json"]?.pathPattern ?? "./messages/{locale}.json"; return settings.locales.map((locale) => ({ path: pattern.replace("{locale}", locale), locale, })); }, importFiles: async ({ files }) => { const bundles = []; const messages = []; const variants = []; for (const file of files) { const json = JSON.parse(new TextDecoder().decode(file.content)); for (const [key, value] of Object.entries(json)) { bundles.push({ id: key, declarations: [] }); messages.push({ bundleId: key, locale: file.locale, selectors: [], }); variants.push({ messageBundleId: key, messageLocale: file.locale, matches: [], pattern: [{ type: "text", value: value as string }], }); } } return { bundles, messages, variants }; }, exportFiles: async ({ messages, variants }) => { const filesByLocale: Record> = {}; for (const message of messages) { const variant = variants.find((v) => v.messageId === message.id); const text = variant?.pattern .filter((p) => p.type === "text") .map((p) => p.value) .join("") ?? ""; if (!filesByLocale[message.locale]) { filesByLocale[message.locale] = {}; } filesByLocale[message.locale][message.bundleId] = text; } return Object.entries(filesByLocale).map(([locale, content]) => ({ locale, name: `${locale}.json`, content: new TextEncoder().encode(JSON.stringify(content, null, 2)), })); }, }; ``` -------------------------------- ### Pluralization Example Source: https://inlang.com/m/gerre34r/library-inlang-paraglideJs?utm_source=chatgpt.com Demonstrates pluralization using message functions with a 'count' parameter. This works correctly for complex locales. ```javascript // Pluralization example m.items_in_cart({ count: 1 }); // "1 item in cart" m.items_in_cart({ count: 5 }); // "5 items in cart" // Works correctly for complex locales (Russian, Arabic, etc.) ``` -------------------------------- ### Minimal Plugin Example Source: https://inlang.com/docs/plugin-api A basic implementation of an inlang plugin that handles JSON files for import and export. It maps JSON keys to message IDs and values to text patterns. ```typescript import type { InlangPlugin } from "@inlang/sdk"; export const plugin: InlangPlugin = { key: "plugin.my.json", toBeImportedFiles: async ({ settings }) => { return settings.locales.map((locale) => ({ path: `./messages/${locale}.json`, locale, })); }, importFiles: async ({ files, settings }) => { const bundles = []; const messages = []; const variants = []; for (const file of files) { const json = JSON.parse(new TextDecoder().decode(file.content)); for (const [key, value] of Object.entries(json)) { bundles.push({ id: key, declarations: [] }); messages.push({ bundleId: key, locale: file.locale, selectors: [], }); variants.push({ messageBundleId: key, messageLocale: file.locale, matches: [], pattern: [{ type: "text", value: value as string }], }); } } return { bundles, messages, variants }; }, exportFiles: async ({ bundles, messages, variants, settings }) => { const files: Record> = {}; for (const message of messages) { const variant = variants.find((v) => v.messageId === message.id); const text = variant?.pattern .filter((p) => p.type === "text") .map((p) => p.value) .join(""); if (!files[message.locale]) files[message.locale] = {}; files[message.locale][message.bundleId] = text ?? ""; } return Object.entries(files).map(([locale, content]) => ({ locale, name: `${locale}.json`, content: new TextEncoder().encode(JSON.stringify(content, null, 2)), })); }, }; ``` -------------------------------- ### Get All Bundles Source: https://inlang.com/docs/crud-api Retrieves all bundles from the database. This is a basic read operation on the 'bundle' table. ```typescript const bundles = await project.db .selectFrom("bundle") .selectAll() .execute(); ``` -------------------------------- ### Query All Messages for a Bundle Source: https://inlang.com/docs/data-model Example using Kysely to retrieve all messages associated with a specific bundle ID. ```typescript // Get all messages for a bundle const messages = await project.db .selectFrom("message") .where("bundleId", "=", "greeting") .selectAll() .execute(); ``` -------------------------------- ### Query Missing Translations Source: https://inlang.com/docs/data-model Example using Kysely to find bundles that are missing translations for a specific locale. ```typescript // Find missing translations const missing = await project.db .selectFrom("bundle") .where((eb) => eb.not( eb.exists( eb.selectFrom("message") .where("message.bundleId", "=", eb.ref("bundle.id")), .where("message.locale", "=", "de") ) ) ) .selectAll() .execute(); ``` -------------------------------- ### Update a Bundle Source: https://inlang.com/docs/crud-api Updates an existing bundle identified by its ID. This example shows updating the 'declarations' field. ```typescript await project.db .updateTable("bundle") .set({ declarations: [{ type: "input-variable", name: "count" }] }) .where("id", "=", "greeting") .execute(); ``` -------------------------------- ### Query All Translation Bundles Source: https://inlang.com/docs/write-tool Fetches all translation bundles from the project's database. Useful for getting an overview of available translation keys. ```typescript const bundles = await project.db .selectFrom("bundle") .selectAll() .execute(); console.log(`Found ${bundles.length} translation keys`); ``` -------------------------------- ### Query All Bundles with Messages Source: https://inlang.com/docs/data-model Example using Kysely to retrieve all bundles along with their associated messages using a left join. ```typescript // Get all bundles with their messages const bundles = await project.db .selectFrom("bundle") .leftJoin("message", "message.bundleId", "bundle.id") .selectAll() .execute(); ``` -------------------------------- ### Define Messages in JSON Source: https://inlang.com/m/gerre34r/library-inlang-paraglideJs?utm_source=chatgpt.com Define your translation messages in a JSON file. This example shows a simple greeting with a placeholder for a name. ```json // messages/en.json { "greeting": "Hello {name}!" } ``` -------------------------------- ### Define Plugin Settings Schema Source: https://inlang.com/docs/write-plugin Use TypeBox to define a schema for plugin settings, allowing users to configure plugin behavior. This example defines a `pathPattern` for translation files. ```typescript import { Type } from "@sinclair/typebox"; const PluginSettings = Type.Object({ pathPattern: Type.String({ description: "Path pattern for translation files", examples: ["./messages/{locale}.json"], }), }); export const plugin: InlangPlugin<{ "plugin.my.json": typeof PluginSettings; }> = { key: "plugin.my.json", settingsSchema: PluginSettings, toBeImportedFiles: async ({ settings }) => { const pattern = settings["plugin.my.json"]?.pathPattern ?? "./messages/{locale}.json"; return settings.locales.map((locale) => ({ path: pattern.replace("{locale}", locale), locale, })); }, // ... rest of plugin }; ``` -------------------------------- ### Get Messages by Locale Source: https://inlang.com/docs/crud-api Retrieves all messages for a specific locale. Useful for fetching translations for a given language. ```typescript const messages = await project.db .selectFrom("message") .selectAll() .where("locale", "=", "en") .execute(); ``` -------------------------------- ### Get Bundle by ID Source: https://inlang.com/docs/crud-api Retrieves a specific bundle by its unique identifier. Returns the first matching bundle or undefined if not found. ```typescript const bundle = await project.db .selectFrom("bundle") .selectAll() .where("id", "=", "greeting") .executeTakeFirst(); ``` -------------------------------- ### Use Messages and Locale Functions Source: https://inlang.com/m/gerre34r/library-inlang-paraglideJs?utm_source=chatgpt.com Import and use message functions for translations and runtime functions to get and set the current locale. Supports autocompletion for message keys and parameters. ```javascript import { m } from "./paraglide/messages.js"; import { setLocale, getLocale } from "./paraglide/runtime.js"; // Use messages (typesafe, with autocomplete) m.hello_world(); m.greeting({ name: "Ada" }); // Get/set locale getLocale(); // "en" setLocale("de"); // switches to German ``` -------------------------------- ### Get Messages for a Bundle Source: https://inlang.com/docs/crud-api Retrieves all messages associated with a particular bundle ID. This helps in fetching all translations within a specific category. ```typescript const messages = await project.db .selectFrom("message") .selectAll() .where("bundleId", "=", "greeting") .execute(); ``` -------------------------------- ### Get Variants for a Message Source: https://inlang.com/docs/crud-api Retrieves all variants (different translations or patterns) for a given message ID. Essential for accessing all possible forms of a translation. ```typescript const variants = await project.db .selectFrom("variant") .selectAll() .where("messageId", "=", messageId) .execute(); ``` -------------------------------- ### Initialize Paraglide JS Source: https://inlang.com/m/gerre34r/library-inlang-paraglideJs?utm_source=chatgpt.com Use the CLI to initialize Paraglide JS. This command sets up message files, configures your bundler, and generates typesafe message functions. ```bash npx @inlang/paraglide-js init ``` -------------------------------- ### Project Settings Configuration Source: https://inlang.com/docs/settings Defines the structure and requirements for the project.inlang/settings.json file. ```APIDOC ## Configuration project.inlang/settings.json ### Description The project settings file defines the core configuration for an Inlang project, including base locales, supported locales, and plugin modules. ### Request Body - **$schema** (string) - Optional - URI to the JSON schema. - **baseLocale** (string) - Required - The base locale of the project (BCP-47 tag). - **locales** (string[]) - Required - List of all available locales (BCP-47 tags), must include baseLocale. - **modules** (string[]) - Optional - List of URIs to plugin modules (must end in .js). - **experimental** (object) - Optional - Map of experimental feature names to true. - **plugin.** (object) - Optional - Plugin-specific configuration settings. ### Request Example { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", "locales": ["en", "de", "fr", "es"], "modules": ["https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js"], "plugin.inlang.i18next": { "pathPattern": "./locales/{locale}.json" } } ``` -------------------------------- ### Load unpacked project with file synchronization Source: https://inlang.com/docs/unpacked-project Load an unpacked project and enable automatic syncing between the filesystem and the in-memory database by setting the `syncInterval` option. Changes are imported and exported automatically. ```typescript const project = await loadProjectFromDirectory({ path: "./project.inlang", fs: fs, syncInterval: 1000, // sync every second }); ``` -------------------------------- ### Add a plugin to settings.json Source: https://inlang.com/docs/install-plugin Register a plugin by adding its URL to the modules array in the project settings file. ```json { "baseLocale": "en", "locales": ["en", "de", "fr"], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js" + ] } ``` -------------------------------- ### Configure plugin settings Source: https://inlang.com/docs/install-plugin Define plugin-specific settings using the plugin. key in the settings file. ```json { "baseLocale": "en", "locales": ["en", "de", "fr"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js" ], "plugin.inlang.i18next": { "pathPattern": "./locales/{locale}.json" } } ``` -------------------------------- ### toBeImportedFiles Source: https://inlang.com/docs/plugin-api Discovers which files should be imported from the filesystem. ```APIDOC ## toBeImportedFiles ### Description Discovers which files should be imported from the filesystem. ### Parameters - **settings** (object) - Required - Project settings including plugin-specific config ### Response - **Array** (object) - Array of file descriptors: - **path** (string) - Path to the file - **locale** (string) - Locale this file contains - **metadata** (any) - Optional, passed to importFiles ``` -------------------------------- ### Reference custom local plugin Source: https://inlang.com/docs/install-plugin Point to a custom built plugin file using a relative path. ```json { "modules": ["./plugins/my-plugin.js"] } ``` -------------------------------- ### Define the plugin structure Source: https://inlang.com/docs/write-plugin Initialize a new plugin with a unique namespaced key. ```typescript // my-plugin.ts import type { InlangPlugin } from "@inlang/sdk"; export const plugin: InlangPlugin = { key: "plugin.my.json", }; ``` -------------------------------- ### Configure baseLocale Source: https://inlang.com/docs/settings Sets the primary language for the project using a BCP-47 language tag. ```json { "baseLocale": "en" } ``` -------------------------------- ### Load an unpacked inlang project Source: https://inlang.com/docs/unpacked-project Use `loadProjectFromDirectory` to load an unpacked project from a specified path. Requires the `fs` module. You can then query translations from the project's database. ```typescript import { loadProjectFromDirectory } from "@inlang/sdk"; import fs from "node:fs"; const project = await loadProjectFromDirectory({ path: "./project.inlang", fs: fs, }); // Query translations const messages = await project.db .selectFrom("message") .selectAll() .execute(); ``` -------------------------------- ### Load inlang Project Source: https://inlang.com/docs/write-tool Loads an inlang project from a specified directory. Requires the project path and a file system module. ```typescript import { loadProjectFromDirectory } from "@inlang/sdk"; import fs from "node:fs"; const project = await loadProjectFromDirectory({ path: "./project.inlang", fs, }); ``` -------------------------------- ### File-first vs. Message-first Design Source: https://inlang.com/docs/architecture Compares traditional file-first i18n approaches with inlang's message-first querying. The message-first approach queries messages directly from the database, offering advantages in cross-locale operations and future-proofing. ```javascript // File-first: load each file, find the key manually const en = JSON.parse(fs.readFileSync('en.json')); const de = JSON.parse(fs.readFileSync('de.json')); const greeting = { en: en.greeting, de: de.greeting }; ``` ```javascript // Message-first: query what you need const messages = await project.db .selectFrom('message') .where('bundleId', '=', 'greeting') .selectAll() .execute(); ``` -------------------------------- ### Enable experimental features Source: https://inlang.com/docs/settings Enables specific experimental features by setting their key to true. ```json { "experimental": { "newFeature": true } } ``` -------------------------------- ### Configure plugin settings Source: https://inlang.com/docs/settings Defines settings for specific plugins using the plugin. prefix. ```json { "plugin.inlang.i18next": { "pathPattern": "./locales/{locale}.json" } } ``` -------------------------------- ### Specify files to import Source: https://inlang.com/docs/write-plugin Implement the toBeImportedFiles function to map project locales to specific file paths. ```typescript export const plugin: InlangPlugin = { key: "plugin.my.json", toBeImportedFiles: async ({ settings }) => { // Return one file per locale return settings.locales.map((locale) => ({ path: `./messages/${locale}.json`, locale, })); }, }; ``` -------------------------------- ### Reference local plugin from node_modules Source: https://inlang.com/docs/install-plugin Use a relative path to point to the plugin file within the node_modules directory. ```json { "modules": ["../node_modules/@inlang/plugin-i18next/dist/index.js"] } ``` -------------------------------- ### Complete CLI Tool for Translation Check Source: https://inlang.com/docs/write-tool A comprehensive command-line tool that loads an inlang project, checks for missing translations across all locales, and reports the findings. ```typescript import { loadProjectFromDirectory } from "@inlang/sdk"; import fs from "node:fs"; async function main() { // Load project const project = await loadProjectFromDirectory({ path: "./project.inlang", fs, }); const settings = await project.settings.get(); const bundles = await project.db.selectFrom("bundle").selectAll().execute(); const messages = await project.db.selectFrom("message").selectAll().execute(); // Find missing translations const missing = []; for (const bundle of bundles) { const bundleMessages = messages.filter((m) => m.bundleId === bundle.id); const localesWithTranslation = bundleMessages.map((m) => m.locale); for (const locale of settings.locales) { if (!localesWithTranslation.includes(locale)) { missing.push({ bundleId: bundle.id, locale }); } } } // Report results if (missing.length === 0) { console.log("All translations complete!"); } else { console.log(`Found ${missing.length} missing translations:\n`); for (const { bundleId, locale } of missing) { console.log(` - "${bundleId}" is missing locale "${locale}"`); } process.exit(1); } } main(); ``` -------------------------------- ### Configure modules Source: https://inlang.com/docs/settings Specifies plugin module URIs, which must be valid paths ending in .js. ```json { "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js", "./local-plugin.js" ] } ``` -------------------------------- ### Configure locales Source: https://inlang.com/docs/settings Defines all supported locales in the project, which must include the baseLocale. ```json { "baseLocale": "en", "locales": ["en", "de", "fr", "es"] } ``` -------------------------------- ### Reference Plugin Settings in Plugin Source: https://inlang.com/docs/plugin-api Reference the defined PluginSettings schema when initializing your inlang plugin. This links the schema to your plugin's configuration. ```typescript export const plugin: InlangPlugin<{ "plugin.my.json": typeof PluginSettings; }> = { key: "plugin.my.json", settingsSchema: PluginSettings, // ... }; ``` -------------------------------- ### Expose Plugin Meta Information Source: https://inlang.com/docs/plugin-api Use the `meta` property in your plugin definition to expose plugin-specific APIs, such as document selectors for IDE extensions. ```typescript export const plugin: InlangPlugin = { key: "plugin.my.json", meta: { "app.inlang.ideExtension": { documentSelectors: [{ language: "json" }], }, }, }; ``` -------------------------------- ### importFiles Source: https://inlang.com/docs/plugin-api Parses file content and converts it to the inlang internal data model. ```APIDOC ## importFiles ### Description Parses file content and converts to inlang's data model. ### Parameters - **files** (array) - Required - Array of files to import: - **locale** (string) - The locale - **content** (Uint8Array) - Binary file content - **toBeImportedFilesMetadata** (any) - Metadata from toBeImportedFiles - **settings** (object) - Required - Project settings ### Response - **bundles** (array) - Array of BundleImport - **messages** (array) - Array of MessageImport - **variants** (array) - Array of VariantImport ``` -------------------------------- ### Save Project to Directory in JavaScript Source: https://inlang.com/docs/write-tool Use this function to explicitly save changes to your inlang project when it's in the unpacked format. Ensure you have the necessary file system access. ```javascript import { saveProjectToDirectory } from "@inlang/sdk"; await saveProjectToDirectory({ fs: fs.promises, project, path: "./project.inlang", }); ``` -------------------------------- ### SQL Query: Find Bundles Missing a Locale Source: https://inlang.com/docs/write-tool Uses Kysely's SQL-like syntax to find all bundles that do not have a translation for a specific locale (e.g., 'de'). ```typescript // Find bundles missing a specific locale const missingGerman = await project.db .selectFrom("bundle") .where((eb) => eb.not( eb.exists( eb.selectFrom("message") .where("message.bundleId", "=", eb.ref("bundle.id")), .where("message.locale", "=", "de") ) ) ) .selectAll() .execute(); ``` -------------------------------- ### Save an unpacked inlang project Source: https://inlang.com/docs/unpacked-project Use `saveProjectToDirectory` to persist changes made to an inlang project back to its directory structure on disk. Requires the `fs/promises` module. ```typescript import { saveProjectToDirectory } from "@inlang/sdk"; import fs from "node:fs/promises"; await saveProjectToDirectory({ fs: fs, project: project, path: "./project.inlang", }); ``` -------------------------------- ### Define Plugin Settings with TypeBox Source: https://inlang.com/docs/plugin-api Use TypeBox to define the schema for your plugin's settings. This ensures type safety and provides validation for user configurations. ```typescript import { Type } from "@sinclair/typebox"; export const PluginSettings = Type.Object({ pathPattern: Type.String({ pattern: ".*\\{locale\\}.*\\.json$", description: "Path to translation files", examples: ["./messages/{locale}.json"], }), sort: Type.Optional( Type.Union([Type.Literal("asc"), Type.Literal("desc")]) ), }); ``` -------------------------------- ### Select Nested Bundle with Messages and Variants Source: https://inlang.com/docs/crud-api Retrieves a bundle along with its nested messages and their variants. Requires the '@inlang/sdk' package. The output structure is detailed in the comment. ```typescript import { selectBundleNested } from "@inlang/sdk"; const bundle = await selectBundleNested(project.db) .where("bundle.id", "=", "greeting") .executeTakeFirst(); // Returns: // { // id: "greeting", // declarations: [], // messages: [ // { // id: "...", // locale: "en", // variants: [{ id: "...", pattern: [...] }] // } // ] // } ``` -------------------------------- ### Export data to files Source: https://inlang.com/docs/write-plugin Implement exportFiles to transform the internal data model back into the desired file format. ```typescript exportFiles: async ({ bundles, messages, variants }) => { // Group messages by locale const filesByLocale: Record> = {}; for (const message of messages) { // Find the variant for this message const variant = variants.find((v) => v.messageId === message.id); // Extract text from the pattern const text = variant?.pattern .filter((p) => p.type === "text") .map((p) => p.value) .join("") ?? ""; // Add to the locale's file if (!filesByLocale[message.locale]) { filesByLocale[message.locale] = {}; } filesByLocale[message.locale][message.bundleId] = text; } // Convert to export format return Object.entries(filesByLocale).map(([locale, content]) => ({ locale, name: `${locale}.json`, content: new TextEncoder().encode(JSON.stringify(content, null, 2)), })); }, ``` -------------------------------- ### Insert a Bundle Source: https://inlang.com/docs/crud-api Inserts a new bundle into the database. Bundles serve as containers for messages. ```typescript await project.db .insertInto("bundle") .values({ id: "greeting", declarations: [] }) .execute(); ``` -------------------------------- ### Define ProjectSettings Type Source: https://inlang.com/docs/settings The TypeScript type definition for the project settings structure. ```typescript type ProjectSettings = { $schema?: string; baseLocale: string; locales: string[]; modules?: string[]; experimental?: Record; } ``` -------------------------------- ### Upsert a bundle using direct database access Source: https://inlang.com/docs/crud-api Use this method to insert a new bundle or update an existing one based on its ID. It requires direct access to the project's database instance. ```typescript await project.db .insertInto("bundle") .values({ id: "greeting", declarations: [] }) .onConflict((oc) => oc.column("id").doUpdateSet({ declarations: [] }) ) .execute(); ``` -------------------------------- ### exportFiles Source: https://inlang.com/docs/plugin-api Converts the inlang internal data model back to external file formats. ```APIDOC ## exportFiles ### Description Converts inlang's data model back to files. ### Parameters - **bundles** (array) - Required - All bundles - **messages** (array) - Required - All messages - **variants** (array) - Required - All variants - **settings** (object) - Required - Project settings ### Response - **Array** (object) - Array of files to write: - **locale** (string) - The locale - **name** (string) - Filename (e.g., "en.json") - **content** (Uint8Array) - Binary content ``` -------------------------------- ### Join Bundles and Messages Source: https://inlang.com/docs/crud-api Performs a left join between the 'bundle' and 'message' tables to retrieve all bundles and their associated messages. Selects all columns from both tables. ```typescript const results = await project.db .selectFrom("bundle") .leftJoin("message", "message.bundleId", "bundle.id") .selectAll() .execute(); ``` -------------------------------- ### Use Typed Message Functions Source: https://inlang.com/m/gerre34r/library-inlang-paraglideJs?utm_source=chatgpt.com Import and use the generated typed message functions for type-safe internationalization. The bundler will tree-shake unused messages. ```javascript import { m } from "./paraglide/messages.js"; m.greeting({ name: "World" }); // "Hello World!" — fully typesafe ``` -------------------------------- ### Parse files into the data model Source: https://inlang.com/docs/write-plugin Implement importFiles to convert external JSON content into the internal bundle, message, and variant structure. ```typescript export const plugin: InlangPlugin = { key: "plugin.my.json", toBeImportedFiles: async ({ settings }) => { return settings.locales.map((locale) => ({ path: `./messages/${locale}.json`, locale, })); }, importFiles: async ({ files }) => { const bundles = []; const messages = []; const variants = []; for (const file of files) { // Parse the JSON file const json = JSON.parse(new TextDecoder().decode(file.content)); // Convert each key-value pair to the data model for (const [key, value] of Object.entries(json)) { // One bundle per translation key bundles.push({ id: key, declarations: [], }); // One message per locale messages.push({ bundleId: key, locale: file.locale, selectors: [], }); // One variant with the actual text variants.push({ messageBundleId: key, messageLocale: file.locale, matches: [], pattern: [{ type: "text", value: value as string }], }); } } return { bundles, messages, variants }; }, }; ``` -------------------------------- ### Plugin Interface Definition Source: https://inlang.com/docs/plugin-api Defines the structure of a plugin for inlang, including methods for importing and exporting files and handling settings. ```typescript type InlangPlugin = { key: string; settingsSchema?: TObject; toBeImportedFiles?: (args) => Promise>; importFiles?: (args) => Promise<{ bundles, messages, variants }>; exportFiles?: (args) => Promise>; meta?: Record>; }; ``` -------------------------------- ### Insert Nested Bundle, Messages, and Variants Source: https://inlang.com/docs/crud-api A convenience function to insert a bundle along with its associated messages and variants in a single operation. Requires the '@inlang/sdk' package. ```typescript import { insertBundleNested } from "@inlang/sdk"; await insertBundleNested(project.db, { id: "greeting", declarations: [], messages: [ { id: crypto.randomUUID(), bundleId: "greeting", locale: "en", selectors: [], variants: [ { id: crypto.randomUUID(), messageId: messageId, matches: [], pattern: [{ type: "text", value: "Hello!" }] } ] } ] }); ``` -------------------------------- ### SQL Query: Count Translations Per Locale Source: https://inlang.com/docs/write-tool Uses Kysely to count the number of translations for each locale present in the project. ```typescript // Count translations per locale const counts = await project.db .selectFrom("message") .select("locale") .select((eb) => eb.fn.count("id").as("count")) .groupBy("locale") .execute(); ``` -------------------------------- ### Inlang CRUD Operations Source: https://inlang.com/docs/crud-api Operations for creating, reading, updating, and deleting translation data using the Inlang SDK and Kysely. ```APIDOC ## Create ### Insert a bundle ```javascript await project.db.insertInto("bundle").values({ id: "greeting", declarations: [] }).execute(); ``` ### Insert a message ```javascript await project.db.insertInto("message").values({ id: crypto.randomUUID(), bundleId: "greeting", locale: "en", selectors: [] }).execute(); ``` ### Insert a variant ```javascript await project.db.insertInto("variant").values({ id: crypto.randomUUID(), messageId: messageId, matches: [], pattern: [{ type: "text", value: "Hello world!" }] }).execute(); ``` ## Read ### Get all bundles ```javascript const bundles = await project.db.selectFrom("bundle").selectAll().execute(); ``` ### Get bundle by ID ```javascript const bundle = await project.db.selectFrom("bundle").selectAll().where("id", "=", "greeting").executeTakeFirst(); ``` ## Update ### Update a bundle ```javascript await project.db.updateTable("bundle").set({ declarations: [{ type: "input-variable", name: "count" }] }).where("id", "=", "greeting").execute(); ``` ## Delete ### Delete a bundle ```javascript await project.db.deleteFrom("bundle").where("id", "=", "greeting").execute(); ``` ``` -------------------------------- ### Find Missing Translations Source: https://inlang.com/docs/write-tool Identifies and returns a list of bundles that are missing translations for specific locales within the project. ```typescript async function findMissingTranslations(project) { const settings = await project.settings.get(); const bundles = await project.db.selectFrom("bundle").selectAll().execute(); const messages = await project.db.selectFrom("message").selectAll().execute(); const missing = []; for (const bundle of bundles) { // Get all messages for this bundle const bundleMessages = messages.filter((m) => m.bundleId === bundle.id); const localesWithTranslation = bundleMessages.map((m) => m.locale); // Find which locales are missing for (const locale of settings.locales) { if (!localesWithTranslation.includes(locale)) { missing.push({ bundleId: bundle.id, locale, }); } } } return missing; } ``` -------------------------------- ### Update Nested Bundle, Messages, and Variants Source: https://inlang.com/docs/crud-api Updates a bundle, its messages, and their variants in a single operation. Requires the '@inlang/sdk' package and existing IDs for messages and variants. ```typescript import { updateBundleNested } from "@inlang/sdk"; await updateBundleNested(project.db, { id: "greeting", declarations: [], messages: [ { id: messageId, locale: "en", selectors: [], variants: [ { id: variantId, matches: [], pattern: [{ type: "text", value: "Updated!" }] } ] } ] }); ``` -------------------------------- ### VariantImport Type Definitions Source: https://inlang.com/docs/plugin-api Defines the structure for importing variants, which can reference messages by ID or by bundle/locale. The recommended approach is by bundle/locale. ```typescript // By message ID type VariantImport = { id?: string; messageId: string; matches: Match[]; pattern: Pattern; }; // By bundle/locale (recommended) type VariantImport = { messageBundleId: string; messageLocale: string; matches: Match[]; pattern: Pattern; }; ``` -------------------------------- ### Bundle Type Definition Source: https://inlang.com/docs/data-model Defines the structure of a bundle, which groups translations by key. The 'id' is the stable translation key. ```typescript type Bundle = { id: string; // e.g., "greeting", "error_404" declarations: Declaration[]; } ``` -------------------------------- ### Upsert a bundle with nested messages using a helper function Source: https://inlang.com/docs/crud-api Leverage the `upsertBundleNested` helper function for a more convenient way to upsert bundles that include their associated messages. This function abstracts some of the direct database logic. ```typescript import { upsertBundleNested } from "@inlang/sdk"; await upsertBundleNested(project.db, { id: "greeting", declarations: [], messages: [...] }); ``` -------------------------------- ### Find Missing Translations for a Locale Source: https://inlang.com/docs/crud-api Identifies bundles that are missing translations for a specific locale (e.g., 'de'). It checks for the existence of messages for that locale within each bundle. ```typescript const missingGerman = await project.db .selectFrom("bundle") .where((eb) => eb.not( eb.exists( eb.selectFrom("message") .where("message.bundleId", "=", eb.ref("bundle.id")), .where("message.locale", "=", "de") ) ) ) .selectAll() .execute(); ``` -------------------------------- ### BundleImport Type Definition Source: https://inlang.com/docs/plugin-api Defines the structure for importing translation bundles, including an ID and an array of declarations. ```typescript type BundleImport = { id: string; declarations: Declaration[]; }; ``` -------------------------------- ### Message Type Definition Source: https://inlang.com/docs/data-model Defines the structure of a message, representing a locale-specific translation within a bundle. Includes 'id', 'bundleId', 'locale', and 'selectors'. ```typescript type Message = { id: string; // auto-generated UUID bundleId: string; // references Bundle.id locale: string; // e.g., "en", "de", "fr" selectors: VariableReference[]; } ``` -------------------------------- ### Insert a Message Source: https://inlang.com/docs/crud-api Inserts a new message associated with a specific bundle and locale. A unique ID is generated for each message. ```typescript await project.db .insertInto("message") .values({ id: crypto.randomUUID(), bundleId: "greeting", locale: "en", selectors: [] }) .execute(); ``` -------------------------------- ### MessageImport Type Definition Source: https://inlang.com/docs/plugin-api Defines the structure for importing messages, linking them to a bundle and locale. The ID is auto-generated if omitted. ```typescript type MessageImport = { id?: string; // auto-generated if omitted bundleId: string; locale: string; selectors: VariableReference[]; }; ``` -------------------------------- ### Insert a Variant Source: https://inlang.com/docs/crud-api Inserts a new variant for a message, defining the actual translated text or pattern. Requires a message ID. ```typescript await project.db .insertInto("variant") .values({ id: crypto.randomUUID(), messageId: messageId, matches: [], pattern: [{ type: "text", value: "Hello world!" }] }) .execute(); ``` -------------------------------- ### Variant Type Definition Source: https://inlang.com/docs/data-model Defines the structure of a variant, which is the actual text pattern for a message. It includes 'id', 'messageId', 'matches', and 'pattern'. ```typescript type Variant = { id: string; // auto-generated UUID messageId: string; // references Message.id matches: Match[]; // conditions for this variant pattern: Pattern; // the text content } ``` -------------------------------- ### Add Missing Translation Entry Source: https://inlang.com/docs/write-tool Inserts a new message entry into the database for a specific bundle and locale, without providing the translated text. ```typescript // Add a missing translation await project.db .insertInto("message") .values({ id: crypto.randomUUID(), bundleId: "greeting", locale: "fr", selectors: [], }) .execute(); ``` -------------------------------- ### Update a Variant's Pattern Source: https://inlang.com/docs/crud-api Updates the pattern (e.g., translated text) of an existing variant identified by its ID. Requires the variant's ID. ```typescript await project.db .updateTable("variant") .set({ pattern: [{ type: "text", value: "Updated text" }] }) .where("id", "=", variantId) .execute(); ``` -------------------------------- ### Delete a Variant Source: https://inlang.com/docs/crud-api Deletes a specific variant by its ID. This is the most granular delete operation. ```typescript await project.db .deleteFrom("variant") .where("id", "=", variantId) .execute(); ``` -------------------------------- ### Delete a Bundle Source: https://inlang.com/docs/crud-api Deletes a bundle by its ID. This operation cascades, automatically deleting all associated messages and variants. ```typescript await project.db .deleteFrom("bundle") .where("id", "=", "greeting") .execute(); // Cascades: all messages and variants are deleted ``` -------------------------------- ### Add Translation Variant Text Source: https://inlang.com/docs/write-tool Inserts a translation variant with its associated text pattern for a given message ID. ```typescript // Add the variant with text await project.db .insertInto("variant") .values({ id: crypto.randomUUID(), messageId: messageId, matches: [], pattern: [{ type: "text", value: "Bonjour!" }], }) .execute(); ``` -------------------------------- ### Delete a Message Source: https://inlang.com/docs/crud-api Deletes a specific message by its ID. This operation cascades, automatically deleting all associated variants. ```typescript await project.db .deleteFrom("message") .where("id", "=", messageId) .execute(); // Cascades: all variants are deleted ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.