New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@inlang/paraglide-js

Package Overview
Dependencies
Maintainers
2
Versions
90
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@inlang/paraglide-js - npm Package Compare versions

Comparing version 1.9.1 to 1.10.0

dist/index-f9c48389.js

168

dist/adapter-utils/index.js

@@ -98,5 +98,4 @@ function negotiateLanguagePreferences(accept, availableLanguageTags) {

base = "";
if (!path.startsWith(base)) {
if (!path.startsWith(base))
return void 0;
}
const pathWithoutBase = path.replace(base, "");

@@ -106,43 +105,14 @@ const maybeLang = pathWithoutBase.split("/")[1];

return void 0;
for (const lang of availableLanguageTags) {
if (lang.toLowerCase() === maybeLang.toLowerCase()) {
return lang;
}
}
return void 0;
return availableLanguageTags.map(lower).includes(lower(maybeLang)) ? maybeLang : void 0;
}
const EMPTY = { type: "static", content: "", matched: false };
const lower = (s) => s.toLowerCase();
const STATIC = 0;
const OPTIONAL = 1;
const REST = 2;
const REQUIRED = 4;
const PART_TYPE = 0;
const PART_CONTENT = 1;
const PART_MATCHED = 2;
function sort_routes(routes) {
const segment_cache = /* @__PURE__ */ new Map();
function split(id) {
const parts = [];
let i = 0;
while (i <= id.length) {
const start = id.indexOf("[", i);
if (start === -1) {
parts.push({ type: "static", content: id.slice(i), matched: false });
break;
}
parts.push({ type: "static", content: id.slice(i, start), matched: false });
const type = id[start + 1] === "[" ? "optional" : id[start + 1] === "." ? "rest" : "required";
const delimiter = type === "optional" ? "]]" : "]";
const end = id.indexOf(delimiter, start);
if (end === -1) {
throw new Error(`Invalid route ID ${id}`);
}
const content = id.slice(start, i = end + delimiter.length);
parts.push({
type,
content,
matched: content.includes("=")
});
}
return parts;
}
function get_parts(segment) {
if (!segment_cache.has(segment)) {
segment_cache.set(segment, split(segment));
}
return segment_cache.get(segment);
}
const get_parts = cached(split);
return routes.sort((route_a, route_b) => {

@@ -153,8 +123,12 @@ var _a, _b, _c, _d, _e, _f;

for (let i = 0; i < Math.max(segments_a.length, segments_b.length); i += 1) {
const segment_a = segments_a[i] ?? [EMPTY];
const segment_b = segments_b[i] ?? [EMPTY];
const segment_a = segments_a[i];
const segment_b = segments_b[i];
if (!segment_a)
return -1;
if (!segment_b)
return 1;
for (let j = 0; j < Math.max(segment_a.length, segment_b.length); j += 1) {
const a = segment_a[j];
const b = segment_b[j];
const dynamic = !!(j % 2);
const dynamic = (a == null ? void 0 : a[PART_TYPE]) || (b == null ? void 0 : b[PART_TYPE]);
if (dynamic) {

@@ -165,33 +139,26 @@ if (!a)

return 1;
const next_a = ((_a = segment_a[j + 1]) == null ? void 0 : _a.content) || ((_c = (_b = segments_a[i + 1]) == null ? void 0 : _b[0]) == null ? void 0 : _c.content);
const next_b = ((_d = segment_b[j + 1]) == null ? void 0 : _d.content) || ((_f = (_e = segments_b[i + 1]) == null ? void 0 : _e[0]) == null ? void 0 : _f.content);
if (a.type === "rest" && b.type === "rest") {
if (next_a && next_b)
const next_a = ((_a = segment_a[j + 1]) == null ? void 0 : _a[PART_CONTENT]) || ((_c = (_b = segments_a[i + 1]) == null ? void 0 : _b[0]) == null ? void 0 : _c[PART_CONTENT]);
const next_b = ((_d = segment_b[j + 1]) == null ? void 0 : _d[PART_CONTENT]) || ((_f = (_e = segments_b[i + 1]) == null ? void 0 : _e[0]) == null ? void 0 : _f[PART_CONTENT]);
const both_have_next = next_a && next_b;
const only_a_has_next = next_a && !next_b;
const only_b_has_next = !next_a && next_b;
if ((a[PART_TYPE] && b[PART_TYPE]) === REST) {
if (both_have_next)
continue;
if (next_a)
if (only_a_has_next)
return -1;
if (next_b)
if (only_b_has_next)
return 1;
}
if (a.type === "rest") {
return next_a && !next_b ? -1 : 1;
if (a[PART_TYPE] === REST)
return only_a_has_next ? -1 : 1;
if (b[PART_TYPE] === REST)
return only_b_has_next ? 1 : -1;
if (a[PART_MATCHED] !== b[PART_MATCHED])
return (-1) ** +a[PART_MATCHED];
if (a[PART_TYPE] !== b[PART_TYPE]) {
return (-1) ** +(a[PART_TYPE] > b[PART_TYPE]);
}
if (b.type === "rest") {
return next_b && !next_a ? 1 : -1;
}
if (a.matched !== b.matched) {
return a.matched ? -1 : 1;
}
if (a.type !== b.type) {
if (a.type === "required")
return -1;
if (b.type === "required")
return 1;
}
} else if ((a == null ? void 0 : a.content) !== (b == null ? void 0 : b.content)) {
if (a === EMPTY)
return -1;
if (b === EMPTY)
return 1;
return sort_static(a.content, b.content);
} else if ((a == null ? void 0 : a[PART_CONTENT]) !== (b == null ? void 0 : b[PART_CONTENT])) {
return sort_static(a[PART_CONTENT], b[PART_CONTENT]);
}

@@ -203,24 +170,37 @@ }

}
function split_route_id(id) {
return get_route_segments(
id.replace(/\[\[[^\]]+\]\](?!$)/g, "")
).filter(Boolean);
function cached(fn) {
const cache = /* @__PURE__ */ new Map();
return (arg) => {
if (!cache.has(arg))
cache.set(arg, fn(arg));
return cache.get(arg);
};
}
function split(id) {
const parts = [];
let i = 0;
while (i <= id.length) {
const start = id.indexOf("[", i);
const entirelyStatic = start === -1;
parts.push([STATIC, id.slice(i, entirelyStatic ? void 0 : start), false]);
if (entirelyStatic)
break;
const type = id[start + 1] === "[" ? OPTIONAL : id[start + 1] === "." ? REST : REQUIRED;
const endBrackets = type === OPTIONAL ? "]]" : "]";
const endBracketIdx = id.indexOf(endBrackets, start);
if (endBracketIdx === -1)
throw new Error(`Invalid route definition ${id}`);
const content = id.slice(start, i = endBracketIdx + endBrackets.length);
parts.push([type, content, content.includes("=")]);
}
return parts;
}
const split_route_id = (id) => id.replace(/\[\[[^\]]+\]\](?!$)/g, "").split("/").filter(Boolean);
function sort_static(a, b) {
if (a === b)
return 0;
let i = 0;
while (a[i] || b[i]) {
const char_a = a[i];
const char_b = b[i];
if (char_a !== char_b) {
if (char_a === void 0)
return 1;
if (char_b === void 0)
return -1;
return char_a < char_b ? -1 : 1;
}
i++;
}
return 0;
let idx = 0;
while (a[idx] === b[idx])
idx++;
return !a[idx] ? 1 : !b[idx] ? -1 : a[idx] < b[idx] ? -1 : 1;
}

@@ -310,5 +290,4 @@ const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;

}
if (param.matcher && !matchers[param.matcher]) {
if (param.matcher && !matchers[param.matcher])
return void 0;
}
const matcher = matchers[param.matcher] ?? (() => true);

@@ -365,11 +344,8 @@ if (matcher(value)) {

const params = exec(match, route.params, matchers);
if (!params)
continue;
return { params, id: pathDefinition };
if (params)
return { params, id: pathDefinition };
}
return void 0;
}
function removeTrailingSlash(path) {
return path.endsWith("/") ? path.slice(0, -1) : path;
}
const removeTrailingSlash = (path) => path.endsWith("/") ? path.slice(0, -1) : path;
const get_route_segments = (route) => route.slice(1).split("/");

@@ -376,0 +352,0 @@ function validatePathTranslations(pathTranslations, availableLanguageTags, matchers) {

@@ -0,3 +1,3 @@

import { MessageIndexFunction } from '~/index.js';
import { PathDefinitionTranslations } from './routeDefinitions.js';
import { MessageIndexFunction } from '~/index.js';

@@ -4,0 +4,0 @@ /**

@@ -47,10 +47,2 @@ export type PathDefinitionTranslations<T extends string = string> = {

} | undefined;
/**
* Splits a route id into its segments, removing segments that
* don't affect the path (i.e. groups). The root route is represented by `/`
* and will be returned as `['']`.
* @param {string} route
* @returns string[]
*/
export declare const get_route_segments: (route: string) => string[];
export {};

@@ -0,5 +1,5 @@

import { Command } from 'commander';
import { Logger } from '~/services/logger/index.js';
import { Repository } from '@lix-js/client';
import { CliStep } from '../../utils.js';
import { Repository } from '@lix-js/client';
import { Logger } from '~/services/logger/index.js';
import { Command } from 'commander';

@@ -6,0 +6,0 @@ export declare const initCommand: Command;

@@ -0,3 +1,3 @@

import { Logger } from '~/services/logger/index.js';
import { CliStep } from '../utils.js';
import { Logger } from '~/services/logger/index.js';

@@ -4,0 +4,0 @@ export declare const checkForUncommittedChanges: CliStep<{

@@ -0,5 +1,5 @@

import { InlangProject } from '@inlang/sdk';
import { Repository } from '@lix-js/client';
import { Logger } from '~/services/logger/index.js';
import { CliStep } from '../utils.js';
import { Logger } from '~/services/logger/index.js';
import { Repository } from '@lix-js/client';
import { InlangProject } from '@inlang/sdk';

@@ -6,0 +6,0 @@ export declare const initializeInlangProject: CliStep<{

@@ -0,5 +1,5 @@

import { Logger } from '~/services/logger/index.js';
import { Repository } from '@lix-js/client';
import { CliStep } from '../utils.js';
import { InlangProject } from '@inlang/sdk';
import { CliStep } from '../utils.js';
import { Repository } from '@lix-js/client';
import { Logger } from '~/services/logger/index.js';

@@ -6,0 +6,0 @@ export declare const maybeAddNinja: CliStep<{

@@ -0,5 +1,5 @@

import { Logger } from '~/services/logger/index.js';
import { Repository } from '@lix-js/client';
import { CliStep } from '../utils.js';
import { InlangProject } from '@inlang/sdk';
import { CliStep } from '../utils.js';
import { Repository } from '@lix-js/client';
import { Logger } from '~/services/logger/index.js';

@@ -6,0 +6,0 @@ export declare const maybeAddSherlock: CliStep<{

@@ -0,3 +1,3 @@

import { Logger } from '~/services/logger/index.js';
import { CliStep } from '../utils.js';
import { Logger } from '~/services/logger/index.js';

@@ -4,0 +4,0 @@ export declare const promptForOutdir: CliStep<{

@@ -0,4 +1,4 @@

import { InlangProject } from '@inlang/sdk';
import { CliStep } from '../utils.js';
import { Repository } from '@lix-js/client';
import { CliStep } from '../utils.js';
import { InlangProject } from '@inlang/sdk';

@@ -5,0 +5,0 @@ export declare const runCompiler: CliStep<{

@@ -0,4 +1,4 @@

import { Logger } from '~/services/logger/index.js';
import { CliStep } from '../utils.js';
import { Repository } from '@lix-js/client';
import { CliStep } from '../utils.js';
import { Logger } from '~/services/logger/index.js';

@@ -5,0 +5,0 @@ export declare function updatePackageJson(opt: {

@@ -0,4 +1,4 @@

import { Repository } from '@lix-js/client';
import { CliStep } from '../utils.js';
import { Logger } from '~/services/logger/index.js';
import { CliStep } from '../utils.js';
import { Repository } from '@lix-js/client';

@@ -5,0 +5,0 @@ export declare const maybeChangeTsConfig: CliStep<{

@@ -0,3 +1,3 @@

import { LanguageTag, Message } from '@inlang/sdk';
import { Params } from './paramsType.js';
import { LanguageTag, Message } from '@inlang/sdk';

@@ -4,0 +4,0 @@ type Resource = {

@@ -0,3 +1,3 @@

import { LanguageTag, Message } from '@inlang/sdk';
import { Params } from './paramsType.js';
import { LanguageTag, Message } from '@inlang/sdk';

@@ -4,0 +4,0 @@ export declare const messageIndexFunction: (args: {

export { compile } from './compiler/compile.js';
export { writeOutput } from './services/file-handling/write-output.js';
export { Logger, type LoggerOptions } from './services/logger/index.js';
export { classifyProjectErrors } from './services/error-handling.js';
export type MessageIndexFunction<T extends string> = (params: Record<string, never>, options: {

@@ -5,0 +6,0 @@ languageTag: T;

@@ -0,3 +1,3 @@

import { PostHog } from 'posthog-node';
import { TelemetryEvents } from './events.js';
import { PostHog } from 'posthog-node';

@@ -4,0 +4,0 @@ /**

{
"name": "@inlang/paraglide-js",
"type": "module",
"version": "1.9.1",
"version": "1.10.0",
"license": "Apache-2.0",

@@ -65,9 +65,9 @@ "publishConfig": {

"vitest": "0.34.3",
"@inlang/cross-sell-ninja": "0.0.18",
"@inlang/cross-sell-sherlock": "0.0.4",
"@inlang/cross-sell-ninja": "0.0.32",
"@inlang/language-tag": "1.5.1",
"@inlang/sdk": "0.34.10",
"@inlang/plugin-message-format": "2.2.0",
"@lix-js/client": "1.4.0",
"@lix-js/fs": "1.0.0"
"@lix-js/client": "2.2.0",
"@inlang/sdk": "0.36.0",
"@lix-js/fs": "2.1.0"
},

@@ -74,0 +74,0 @@ "exports": {

@@ -0,1 +1,7 @@

---
title: "Getting Started"
description: "Learn how to install the ParaglideJS i18n library in your project"
---
[![Inlang-ecosystem compatibility badge](https://cdn.jsdelivr.net/gh/opral/monorepo@main/inlang/assets/md-badges/inlang.svg)](https://inlang.com)
# Getting started

@@ -20,3 +26,3 @@

Messages are stored in `messages/{lang}.json`. To add a message simply add a key-value pair. You can add parameters with curly braces.
Messages are stored in `messages/{lang}.json` as key-value pairs. You can add parameters with curly braces.

@@ -37,11 +43,10 @@ ```diff

If you are using Bundler, you can use one of the [Bundler Plugins](#usage-with-a-bundler) to recompile automatically.
If you are using a Bundler use one of the [Bundler Plugins](usage#usage-with-a-bundler) to recompile automatically.
## Using Messages in Code
After running the compiler, you can import messages with `import * as m from "./paraglide/messages"`.
After running the compiler import the messages with `import * as m from "./paraglide/messages"`. By convention, a wildcard import is used.
```js
import * as m from "./paraglide/messages.js"
import { setLanguageTag } from "./paraglide/runtime.js"

@@ -52,190 +57,2 @@ m.hello() // Hello world!

## Working with the Inlang Message Format
Paraglide is part of the highly modular Inlang Ecosystem which supports many different Message Formats. By default, the [Inlang Message Format](https://inlang.com/m/reootnfj/plugin-inlang-messageFormat) is used.
It expects messages to be in `messages/{lang}.json` relative to your repo root.
```json
//messages/en.json
{
//the $schema key is automatically ignored
"$schema": "https://inlang.com/schema/inlang-message-format",
"hello_world: "Hello World!",
"greeting": "Hello {name}!"
}
```
The `messages/{lang}.json` file contains a flat map of message IDs and their translations. You can use curly braces to insert `{parameters}` into translations
**Nesting purposely isn't supported and likely won't be**. Nested messages are way harder to interact with from complementary tools like the [Sherlock IDE Extension](https://inlang.com/m/r7kp499g/app-inlang-ideExtension), the [Parrot Figma Plugin](https://inlang.com/m/gkrpgoir/app-parrot-figmaPlugin), or the [Fink Localization editor](https://inlang.com/m/tdozzpar/app-inlang-finkLocalizationEditor). Intellisense also becomes less helpful since it only shows the messages at the current level, not all messages. Additionally enforcing an organization-style side-steps organization discussions with other contributors.
### Complex Formatting
The Message Format is still quite young, so advanced formats like plurals, param-formatting, and markup interpolation are currently not supported but are all on our roadmap.
If you need complex formatting, like plurals, dates, currency, or markup interpolation you can achieve them like so:
For a message with multiple cases, aka a _select message_, you can define a message for each case & then use a Map in JS to index into it.
```ts
import * as m from "./paraglide/messages.js"
const season = {
spring: m.spring,
summer: m.summer,
autumn: m.autumn,
winter: m.winter,
} as const
const msg = season["spring"]() // Hello spring!
```
For date & currency formatting use the `.toLocaleString` method on the `Date` or `Number`.
```ts
import * as m from "./paraglide/messages.js"
import { languageTag } from "./paraglide/runtime.js"
const todaysDate = new Date();
m.today_is_the({
date: todaysDate.toLocaleString(languageTag())
})
const price = 100;
m.the_price_is({
price: price.toLocaleString(languageTag(), {
style: "currency",
currency: "EUR",
})
})
```
You can put HTML into the messages. This is useful for links and images.
```json
// messages/en.json
{
"you_must_agree_to_the_tos": "You must agree to the <a href='/en/tos'>Terms of Service</a>."
}
```
```json
// messages/de.json
{
you_must_agree_to_the_tos": "Sie müssen den <a href='/de/agb'>Nutzungsbedingungen</a> zustimmen."
}
```
There is currently no way to interpolate full-blown components into the messages. If you require components mid-message you will need to create a one-off component for that bit of text.
## Setting the language
You can set the [language tag](https://www.inlang.com/m/8y8sxj09/library-inlang-languageTag) by calling `setLanguageTag()` with the desired language, or a getter function. Any subsequent calls to either `languageTag()` or a message function will use the new language tag.
```js
import { setLanguageTag } from "./paraglide/runtime.js"
import * as m from "./paraglide/messages.js"
setLanguageTag("de")
m.hello() // Hallo Welt!
setLanguageTag(()=>document.documentElement.lang /* en */ )
m.hello() // Hello world!
```
The [language tag](https://www.inlang.com/m/8y8sxj09/library-inlang-languageTag) is global, so you need to be careful with it on the server to make sure multiple requests don't interfere with each other. Always use a getter-function that returns the current language tag _for the current request_.
You will need to call `setLanguageTag` on both the server and the client since they run in separate processes.
## Reacting to language changes
Messages aren't reactive, so you will need to trigger a re-render when the language changes. You can register a callback using `onSetLanguageTag()`. It is called whenever the [language tag](https://www.inlang.com/m/8y8sxj09/library-inlang-languageTag) changes.
If you are using a [framework-specific library](#use-it-with-your-favorite-framework) this is done for you.
```js
import { setLanguageTag, onSetLanguageTag } from "./paraglide/runtime.js"
import * as m from "./paraglide/messages.js"
onSetLanguageTag((newLanguageTag) => {
console.log(`The language changed to ${newLanguageTag}`)
})
setLanguageTag("de") // The language changed to de
setLanguageTag("en") // The language changed to en
```
Things to know about `onSetLanguageTag()`:
- You can only register one listener. If you register a second listener it will throw an error.
- `onSetLanguageTag` shouldn't be used on the server.
## Getting a message in a specific language
You can import a message in a specific language from `paraglide/messages/{lang}.js`.
```ts
import * as m from "./paraglide/messages/de.js"
m.hello() // Hallo Welt
```
If you want to force a language, but don't know _which_ language ahead of time you can pass the `languageTag` option as the second parameter to a message function. This is often handy on the server.
```js
import * as m from "./paraglide/messages.js"
const msg = m.hello({ name: "Samuel" }, { languageTag: "de" }) // Hallo Samuel!
```
## Lazy-Loading
Paraglide consciously discourages lazy-loading translations since it causes a render-fetch waterfall which seriously hurts your Web Vitals. Learn more about why lazy-loading is bad & what to do instead in [our blog post on lazy-loading](https://inlang.com/g/mqlyfa7l/guide-lorissigrist-dontlazyload).
If you want to do it anyway, lazily import the language-specific message files.
```ts
const lazyGerman = await import("./paraglide/messages/de.js")
lazyGerman.hello() // Hallo Welt
```
## Usage with a Bundler
If you are using a bundler you should use the corresponding plugin. The plugin will keep your Message Functions up-to-date by compiling whenever your messages change and before building your app.
<doc-links>
<doc-link title="Vite Plugin" icon="tabler:brand-vite" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-vite" description="Go to Github"></doc-link>
<doc-link title="Rollup Plugin" icon="file-icons:rollup" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-rollup" description="Go to Github"></doc-link>
<doc-link title="Webpack Plugin" icon="mdi:webpack" href="https://github.com/opral/monorepo/tree/main/inlang/source-code/paraglide/paraglide-webpack" description="Go to Github"></doc-link>
</doc-links>
## Configuration
Most of the configuration is done in `./project.inlang/settings.json`, except for paraglide's output directory, which needs to be passed in when calling the compiler.
### Languages
You can declare which languages you support in the `languageTags` array.
```json
// project.inlang/settings.json
{
"languageTags": ["en", "de"]
}
```
Create the corresponding `messages/{lang}.json` files and get translating!
### Moving the Translation Files
If you want your language files to be in a different location you can change the `pathPattern` of the [Inlang-Message-Format plugin](https://inlang.com/m/reootnfj/plugin-inlang-messageFormat).
```diff
// project.inlang/settings.json
{
"plugin.inlang.messageFormat": {
- "pathPattern": "./messages/{languageTag}.json"
+ "pathPattern": "./i18n/{languageTag}.json"
}
}
```
# Playground

@@ -242,0 +59,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc