@inlang/paraglide-js 1.0.0-prerelease.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -0
- package/bin/run.js +5 -0
- package/dist/cli/commands/compile.d.ts +3 -0
- package/dist/cli/commands/compile.d.ts.map +1 -0
- package/dist/cli/commands/compile.js +57 -0
- package/dist/cli/main.d.ts +11 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +40 -0
- package/dist/compiled-output/example-javascript/messages.d.ts +7 -0
- package/dist/compiled-output/example-javascript/messages.js +42 -0
- package/dist/compiled-output/example-javascript/runtime.d.ts +38 -0
- package/dist/compiled-output/example-javascript/runtime.js +112 -0
- package/dist/compiled-output/example-typescript/messages.d.ts +7 -0
- package/dist/compiled-output/example-typescript/messages.js +42 -0
- package/dist/compiled-output/example-typescript/runtime.d.ts +38 -0
- package/dist/compiled-output/example-typescript/runtime.js +112 -0
- package/dist/compiled-output/nextjs-example/messages.d.ts +7 -0
- package/dist/compiled-output/nextjs-example/messages.js +42 -0
- package/dist/compiled-output/nextjs-example/runtime.d.ts +38 -0
- package/dist/compiled-output/nextjs-example/runtime.js +112 -0
- package/dist/compiled-output/react-example/messages.d.ts +7 -0
- package/dist/compiled-output/react-example/messages.js +42 -0
- package/dist/compiled-output/react-example/runtime.d.ts +38 -0
- package/dist/compiled-output/react-example/runtime.js +112 -0
- package/dist/compiled-output/svelte-example/messages.d.ts +7 -0
- package/dist/compiled-output/svelte-example/messages.js +42 -0
- package/dist/compiled-output/svelte-example/runtime.d.ts +38 -0
- package/dist/compiled-output/svelte-example/runtime.js +112 -0
- package/dist/compiled-output/sveltekit-example/messages.d.ts +7 -0
- package/dist/compiled-output/sveltekit-example/messages.js +42 -0
- package/dist/compiled-output/sveltekit-example/runtime.d.ts +38 -0
- package/dist/compiled-output/sveltekit-example/runtime.js +112 -0
- package/dist/compiler/compile.d.ts +15 -0
- package/dist/compiler/compile.d.ts.map +1 -0
- package/dist/compiler/compile.js +170 -0
- package/dist/compiler/compile.test.d.ts +2 -0
- package/dist/compiler/compile.test.d.ts.map +1 -0
- package/dist/compiler/compile.test.js +302 -0
- package/dist/compiler/compileMessage.d.ts +3 -0
- package/dist/compiler/compileMessage.d.ts.map +1 -0
- package/dist/compiler/compileMessage.js +44 -0
- package/dist/compiler/compileMessage.test.d.ts +2 -0
- package/dist/compiler/compileMessage.test.d.ts.map +1 -0
- package/dist/compiler/compileMessage.test.js +91 -0
- package/dist/compiler/compilePattern.d.ts +18 -0
- package/dist/compiler/compilePattern.d.ts.map +1 -0
- package/dist/compiler/compilePattern.js +28 -0
- package/dist/compiler/compilePattern.test.d.ts +2 -0
- package/dist/compiler/compilePattern.test.d.ts.map +1 -0
- package/dist/compiler/compilePattern.test.js +26 -0
- package/dist/compiler/jsdocFromParams.d.ts +12 -0
- package/dist/compiler/jsdocFromParams.d.ts.map +1 -0
- package/dist/compiler/jsdocFromParams.js +17 -0
- package/dist/compiler/jsdocFromParams.test.d.ts +2 -0
- package/dist/compiler/jsdocFromParams.test.d.ts.map +1 -0
- package/dist/compiler/jsdocFromParams.test.js +6 -0
- package/dist/compiler/paramsType.d.ts +12 -0
- package/dist/compiler/paramsType.d.ts.map +1 -0
- package/dist/compiler/paramsType.js +17 -0
- package/dist/compiler/paramsType.test.d.ts +2 -0
- package/dist/compiler/paramsType.test.d.ts.map +1 -0
- package/dist/compiler/paramsType.test.js +6 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @inlang/paraglide-js
|
|
2
|
+
|
|
3
|
+
## ATTENTION: Paraglide is in pre-release mode. Discuss the API at https://github.com/inlang/monorepo/discussions/1464
|
|
4
|
+
|
|
5
|
+
<doc-gallery>TODO: adapter gallery</doc-gallery>
|
|
6
|
+
|
|
7
|
+
- [x] the smallest, fastest, and most typesafe i18n library
|
|
8
|
+
- [x] only bundles the messages that are used (tree-shaking)
|
|
9
|
+
- [x] storage agnostic (JSON, YAML, CSV, etc.)
|
|
10
|
+
- [x] plug & play with the [inlang ecosystem](https://inlang.com/marketplace)
|
|
11
|
+
|
|
12
|
+
# Usage
|
|
13
|
+
|
|
14
|
+
Messages are imported as a namespace and can be used as follows:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// m is a namespace that contains all messages of your project
|
|
18
|
+
// a bundler like rollup or webpack only bundles
|
|
19
|
+
// the messages that are used
|
|
20
|
+
import * as m from "@inlang/paraglide-js/messages"
|
|
21
|
+
import { setLanguageTag } from "@inlang/paraglide-js"
|
|
22
|
+
|
|
23
|
+
// use a message
|
|
24
|
+
m.hello() // Hello world!
|
|
25
|
+
|
|
26
|
+
// message with parameters
|
|
27
|
+
m.loginHeader({ name: "Samuel" }) // Hello Samuel, please login to continue.
|
|
28
|
+
|
|
29
|
+
// change the language
|
|
30
|
+
setLanguageTag("de")
|
|
31
|
+
|
|
32
|
+
m.loginHeader({ name: "Samuel" }) // Hallo Samuel, bitte melde dich an, um fortzufahren.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Paraglide JS exports four runtime variables and functions via "@inlang/paraglide-js":
|
|
37
|
+
|
|
38
|
+
- `sourceLanguageTag`: the source language tag of the project
|
|
39
|
+
- `languageTags`: all language tags of the current project
|
|
40
|
+
- `languageTag()`: returns the language tag of the current user
|
|
41
|
+
- `setLanguageTag()`: sets the language tag of the current user
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Getting started
|
|
45
|
+
|
|
46
|
+
## Available Adapters
|
|
47
|
+
|
|
48
|
+
- TODO: add adapters
|
|
49
|
+
|
|
50
|
+
## Standalone
|
|
51
|
+
|
|
52
|
+
1. Add paraglide as a dependency:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @inlang/paraglide-js
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
2. Add the compiler to your build script:
|
|
59
|
+
|
|
60
|
+
```diff
|
|
61
|
+
{
|
|
62
|
+
"scripts": {
|
|
63
|
+
+ "build": "paraglide-js compile --namespace <your project name>"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Architecture
|
|
70
|
+
|
|
71
|
+
Inlang Paraglide JS leverages a compiler to emit a use-case optimized i18n library.
|
|
72
|
+
|
|
73
|
+
By leveraging a compiler, inlang Paraglide JS eliminates a class of edge cases while also being simpler, faster, and more reliable than other i18n libraries. The compiled runtime contains less than 50 LOC (lines of code) and is less than 1kb gzipped.
|
|
74
|
+
|
|
75
|
+
Inlang Paraglide-JS consists of four main parts:
|
|
76
|
+
|
|
77
|
+
- `COMPILER`: compiles messages into tree-shakable message functions
|
|
78
|
+
- `MESSAGES`: the compiled tree-shakable message functions
|
|
79
|
+
- `RUNTIME`: a runtime that resolves the language tag of the current user
|
|
80
|
+
- `ADAPTER`: (if required) an adapter that adjust the runtime for different frameworks
|
|
81
|
+
|
|
82
|
+
<img src="./assets/architecture.svg">
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## COMPILER
|
|
86
|
+
|
|
87
|
+
The compiler loads an inlang project and compiles the messages into tree-shakable and type safe message functions.
|
|
88
|
+
|
|
89
|
+
### Example
|
|
90
|
+
|
|
91
|
+
#### Input
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
// messages/en.json
|
|
95
|
+
|
|
96
|
+
hello: "Hello {name}!"
|
|
97
|
+
|
|
98
|
+
loginButton: "Login"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Output
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
// @inlang/paraglide-js/messages
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {object} params
|
|
108
|
+
* @param {string} params.name
|
|
109
|
+
*/
|
|
110
|
+
function hello({ name }) {
|
|
111
|
+
return `Hello ${name}!`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function loginButton() {
|
|
115
|
+
return "Login"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## MESSAGES
|
|
121
|
+
|
|
122
|
+
The compiled messages are importable as a namespace import (`import * as m`).
|
|
123
|
+
|
|
124
|
+
The namespace import ensures that bundlers like Rollup, Webpack, or Turbopack can tree-shake the messages that are not used.
|
|
125
|
+
|
|
126
|
+
### Example
|
|
127
|
+
|
|
128
|
+
Three compiled message functions exists in an examplary project.
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
// @inlang/paraglide-js/<namespace>/messages
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
export function hello(params) {
|
|
135
|
+
return `Hello ${params.name}!`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function loginButton() {
|
|
139
|
+
return "Login"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function loginHeader(params) {
|
|
143
|
+
return `Hello ${params.name}, please login to continue.`
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
Only the message `hello` is used in the source code.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// source/index.js
|
|
152
|
+
|
|
153
|
+
import * as m from "@inlang/paraglide-js/<namespace>/messages"
|
|
154
|
+
|
|
155
|
+
console.log(m.hello({ name: "Samuel" }))
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The bundler tree-shakes (removes) `loginButton` and `loginHeader` and only includes `hello` in the output.
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
// output/index.js
|
|
162
|
+
|
|
163
|
+
function hello(params) {
|
|
164
|
+
return `Hello ${params.name}!`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(hello({ name: "Samuel"}))
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
## RUNTIME
|
|
172
|
+
|
|
173
|
+
View the source of your imports from `@inlang/paraglide-js/<namespace>` to find the latest runtime API and documentation.
|
|
174
|
+
|
|
175
|
+
## ADAPTER
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
Paraglide-JS can be adapted to any framework or environment by calling `setLanguageTag()` and `onSetLanguageTag()`.
|
|
180
|
+
|
|
181
|
+
1. `setLanguageTag()` can be used to set a getter function for the language tag. The getter function can be used to resolve server side language tags or to resolve the language tag from a global state management library like Redux or Vuex.
|
|
182
|
+
2. `onSetLanguageTag()` can be used to trigger side-effects such as updating the UI, or request the site in the new language from the server.
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
### Writing an Adapter
|
|
186
|
+
|
|
187
|
+
The following example adapts Paraglide-JS to a fictious metaframework like NextJS, SolidStart, SvelteKit, or Nuxt.
|
|
188
|
+
|
|
189
|
+
The goal is to provide a high-level understanding of how to adapt Paraglide-JS to a framework. Besides this example, we recommend to view the source-code of available adapters. In general, only two functions need to be called to adapt Paraglide-JS to a framework:
|
|
190
|
+
|
|
191
|
+
1. `setLanguageTag()`: to set the language tag
|
|
192
|
+
2. `onSetLanguageTag()`: to trigger a side-effect when the language changes
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import { setLanguageTag, onSetLanguageTag } from "@inlang/paraglide-js/<namespace>"
|
|
199
|
+
import { isServer, request, render } from "@example/framework"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
// On a server, the language tag needs to be resolved on a
|
|
203
|
+
// per-request basis. Hence, we need to pass a getter
|
|
204
|
+
// function () => string to setLanguageTag.
|
|
205
|
+
//
|
|
206
|
+
// Most frameworks offer a way to access the current
|
|
207
|
+
// request. In this example, we assume that the language tag
|
|
208
|
+
// is available in the request object.
|
|
209
|
+
if (isServer){
|
|
210
|
+
setLanguageTag(() => request.languageTag)
|
|
211
|
+
}
|
|
212
|
+
// On a client, the language tag could be resolved from
|
|
213
|
+
// the document's html lang tag.
|
|
214
|
+
//
|
|
215
|
+
// In addition, we also want to trigger a side-effect
|
|
216
|
+
// to request the site if the language changes.
|
|
217
|
+
else {
|
|
218
|
+
setLanguageTag(() => document.documentElement.lang)
|
|
219
|
+
|
|
220
|
+
//! Make sure to call `onSetLanguageTag` after
|
|
221
|
+
//! the initial language tag has been set to
|
|
222
|
+
//! avoid an infinite loop.
|
|
223
|
+
|
|
224
|
+
// route to the page in the new language
|
|
225
|
+
onSetLanguageTag((newLanguageTag) => {
|
|
226
|
+
window.location.pathname = `/${newLanguageTag}${window.location.pathname}`
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// render the app
|
|
231
|
+
render((page) =>
|
|
232
|
+
<html lang={request.languageTag}>
|
|
233
|
+
<body>
|
|
234
|
+
{page}
|
|
235
|
+
</body>
|
|
236
|
+
</html>
|
|
237
|
+
)
|
|
238
|
+
```
|
|
239
|
+
|
package/bin/run.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/compile.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,eAAO,MAAM,cAAc,SAsBxB,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { loadProject } from "@inlang/sdk";
|
|
2
|
+
import consola from "consola";
|
|
3
|
+
import { compile } from "../../compiler/compile.js";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { paraglideDirectory } from "../main.js";
|
|
8
|
+
import dedent from "dedent";
|
|
9
|
+
export const compileCommand = new Command()
|
|
10
|
+
.name("compile")
|
|
11
|
+
.summary("Compiles an inlang project into an importable library.")
|
|
12
|
+
.requiredOption("--project <path>", "The path to the project settings file.", "./project.inlang.json")
|
|
13
|
+
.requiredOption("--namespace <name>", dedent `
|
|
14
|
+
The import namespace of the compiled library.
|
|
15
|
+
\nExample: --namespace frontend
|
|
16
|
+
-> import * as m from "@inlang/paraglide-js/frontend/messages"
|
|
17
|
+
\n`)
|
|
18
|
+
.action((options) => {
|
|
19
|
+
runCompileCommand({
|
|
20
|
+
projectPath: options.project,
|
|
21
|
+
namespace: options.namespace,
|
|
22
|
+
paraglideDirectory,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
const runCompileCommand = async (args) => {
|
|
26
|
+
consola.info(`Compiling inlang project at "${args.projectPath}".`);
|
|
27
|
+
const project = exitIfErrors(await loadProject({ settingsFilePath: resolve(process.cwd(), args.projectPath), nodeishFs: fs }));
|
|
28
|
+
const output = compile({
|
|
29
|
+
messages: project.query.messages.getAll(),
|
|
30
|
+
settings: project.settings(),
|
|
31
|
+
});
|
|
32
|
+
const outputDirectory = `${args.paraglideDirectory}/dist/compiled-output` + (args.namespace ? `/${args.namespace}` : "");
|
|
33
|
+
for (const [fileName, fileContent] of Object.entries(output)) {
|
|
34
|
+
// create the compiled-output directory if it doesn't exist
|
|
35
|
+
await fs.access(outputDirectory).catch(async () => {
|
|
36
|
+
await fs.mkdir(outputDirectory, { recursive: true });
|
|
37
|
+
});
|
|
38
|
+
await fs.writeFile(`${outputDirectory}/${fileName}`, fileContent, {
|
|
39
|
+
encoding: "utf-8",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
consola.success("Successfully compiled the project.");
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Utility function to exit when the project has errors.
|
|
46
|
+
*/
|
|
47
|
+
const exitIfErrors = (project) => {
|
|
48
|
+
if (project.errors().length > 0) {
|
|
49
|
+
// two console statements for better output formatting
|
|
50
|
+
// because project.errors() internal .toString() method
|
|
51
|
+
// is better than manual processing.
|
|
52
|
+
consola.error(`The project has errors:`);
|
|
53
|
+
consola.log(project.errors());
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
return project;
|
|
57
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* The absolute path to the paraglide directory.
|
|
4
|
+
*
|
|
5
|
+
* slices a path
|
|
6
|
+
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
7
|
+
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
8
|
+
*/
|
|
9
|
+
export declare const paraglideDirectory: string;
|
|
10
|
+
export declare const cli: Command;
|
|
11
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUnC;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAA4D,CAAA;AAE7F,eAAO,MAAM,GAAG,SAqBb,CAAA"}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import consola from "consola";
|
|
2
|
+
import dedent from "dedent";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { compileCommand } from "./commands/compile.js";
|
|
6
|
+
/**
|
|
7
|
+
* The path this file is executed from.
|
|
8
|
+
*
|
|
9
|
+
* Usually something like '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
10
|
+
*/
|
|
11
|
+
const metaUrlPath = fileURLToPath(import.meta.url);
|
|
12
|
+
/**
|
|
13
|
+
* The absolute path to the paraglide directory.
|
|
14
|
+
*
|
|
15
|
+
* slices a path
|
|
16
|
+
* from '/Users/samuel/example/repository/node_modules/paraglide-js/dist/cli/main.js'
|
|
17
|
+
* to '/Users/samuel/example/repository/node_modules/paraglide-js'
|
|
18
|
+
*/
|
|
19
|
+
export const paraglideDirectory = metaUrlPath.slice(0, metaUrlPath.indexOf("/dist/"));
|
|
20
|
+
export const cli = new Command()
|
|
21
|
+
.name("paraglide-js")
|
|
22
|
+
.addCommand(compileCommand)
|
|
23
|
+
.hook("preAction", () => {
|
|
24
|
+
// ------------------- VALIDATE IF RUNNING FROM CORRECT FOLDER -------------------
|
|
25
|
+
// the CLI expects to be running from the dist folder of the specific paraglide package
|
|
26
|
+
// to compile the output in the correct directory
|
|
27
|
+
if (metaUrlPath.includes(`paraglide-js/dist/`) === false) {
|
|
28
|
+
consola.error(dedent `
|
|
29
|
+
The CLI is not running from the dist folder.
|
|
30
|
+
|
|
31
|
+
This is likely an internal bug. Please file an issue at https://github.com/inlang/monorepo/issues.
|
|
32
|
+
|
|
33
|
+
Debug information:
|
|
34
|
+
|
|
35
|
+
- metaUrlPath: ${metaUrlPath}
|
|
36
|
+
`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
cli.showHelpAfterError(true);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import { languageTag } from "./runtime.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This message has been compiled by [inlang paraglide](https://inlang.com/marketplace/library.inlang.paraglideJs).
|
|
7
|
+
*
|
|
8
|
+
* - Don't edit the message manually. Use the [inlang ide extension](https://inlang.com/marketplace/app.inlang.ideExtension)
|
|
9
|
+
* or the [web editor](https://inlang.com/marketplace/app.inlang.editor) to edit the message.
|
|
10
|
+
*
|
|
11
|
+
* - The params are NonNullable<unknown> because inlang can't know the value type of a param (yet).
|
|
12
|
+
*
|
|
13
|
+
* @param {{ languageTag: NonNullable<unknown> }} params
|
|
14
|
+
* @returns {string}
|
|
15
|
+
*/
|
|
16
|
+
export const currentLanguageTag = (params) => {
|
|
17
|
+
const variants = {
|
|
18
|
+
"en": `The current language tag is "${params.languageTag}".`,
|
|
19
|
+
"de": `Der aktuelle Sprachtag ist "${params.languageTag}".`
|
|
20
|
+
}
|
|
21
|
+
return variants[languageTag()] ?? "currentLanguageTag"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This message has been compiled by [inlang paraglide](https://inlang.com/marketplace/library.inlang.paraglideJs).
|
|
27
|
+
*
|
|
28
|
+
* - Don't edit the message manually. Use the [inlang ide extension](https://inlang.com/marketplace/app.inlang.ideExtension)
|
|
29
|
+
* or the [web editor](https://inlang.com/marketplace/app.inlang.editor) to edit the message.
|
|
30
|
+
*
|
|
31
|
+
* - The params are NonNullable<unknown> because inlang can't know the value type of a param (yet).
|
|
32
|
+
*
|
|
33
|
+
* @param {{ name: NonNullable<unknown>, count: NonNullable<unknown> }} params
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
export const greeting = (params) => {
|
|
37
|
+
const variants = {
|
|
38
|
+
"en": `Welcome ${params.name}! You have ${params.count} messages.`,
|
|
39
|
+
"de": `Hallo ${params.name}! Du hast ${params.count} Nachrichten.`
|
|
40
|
+
}
|
|
41
|
+
return variants[languageTag()] ?? "greeting"
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project's source language tag.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* if (newlySelectedLanguageTag === sourceLanguageTag){
|
|
6
|
+
* // do nothing as the source language tag is the default language
|
|
7
|
+
* return
|
|
8
|
+
* }
|
|
9
|
+
*/
|
|
10
|
+
export const sourceLanguageTag: "en";
|
|
11
|
+
/**
|
|
12
|
+
* The project's available language tags.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* if (availableLanguageTags.includes(userSelectedLanguageTag) === false){
|
|
16
|
+
* throw new Error("Language tag not available")
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
export const availableLanguageTags: readonly ["en", "de"];
|
|
20
|
+
/**
|
|
21
|
+
* Get the current language tag.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* if (languageTag() === "de"){
|
|
25
|
+
* console.log("Germany 🇩🇪")
|
|
26
|
+
* } else if (languageTag() === "nl"){
|
|
27
|
+
* console.log("Netherlands 🇳🇱")
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* @type {() => AvailableLanguageTag}
|
|
31
|
+
*/
|
|
32
|
+
export let languageTag: () => AvailableLanguageTag;
|
|
33
|
+
export function setLanguageTag(tag: AvailableLanguageTag | (() => AvailableLanguageTag)): void;
|
|
34
|
+
export function onSetLanguageTag(fn: (languageTag: any) => void): void;
|
|
35
|
+
/**
|
|
36
|
+
* A language tag that is available in the project.
|
|
37
|
+
*/
|
|
38
|
+
export type AvailableLanguageTag = (typeof availableLanguageTags)[number];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
|
|
2
|
+
/** @type {((tag: AvailableLanguageTag) => void) | undefined} */
|
|
3
|
+
let _onSetLanguageTag
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The project's source language tag.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* if (newlySelectedLanguageTag === sourceLanguageTag){
|
|
10
|
+
* // do nothing as the source language tag is the default language
|
|
11
|
+
* return
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
export const sourceLanguageTag = "en"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The project's available language tags.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* if (availableLanguageTags.includes(userSelectedLanguageTag) === false){
|
|
21
|
+
* throw new Error("Language tag not available")
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
export const availableLanguageTags = /** @type {const} */ (["en","de"])
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the current language tag.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* if (languageTag() === "de"){
|
|
31
|
+
* console.log("Germany 🇩🇪")
|
|
32
|
+
* } else if (languageTag() === "nl"){
|
|
33
|
+
* console.log("Netherlands 🇳🇱")
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* @type {() => AvailableLanguageTag}
|
|
37
|
+
*/
|
|
38
|
+
export let languageTag = () => sourceLanguageTag
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set the language tag.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
*
|
|
45
|
+
* // changing to language
|
|
46
|
+
* setLanguageTag("en")
|
|
47
|
+
*
|
|
48
|
+
* // passing a getter function also works.
|
|
49
|
+
* //
|
|
50
|
+
* // a getter function is useful for resolving a language tag
|
|
51
|
+
* // on the server where every request has a different language tag
|
|
52
|
+
* setLanguageTag(() => {
|
|
53
|
+
* return request.langaugeTag
|
|
54
|
+
* })
|
|
55
|
+
*
|
|
56
|
+
* @param {AvailableLanguageTag | (() => AvailableLanguageTag)} tag
|
|
57
|
+
*/
|
|
58
|
+
export const setLanguageTag = (tag) => {
|
|
59
|
+
if (typeof tag === "function") {
|
|
60
|
+
languageTag = tag
|
|
61
|
+
} else {
|
|
62
|
+
languageTag = () => tag
|
|
63
|
+
}
|
|
64
|
+
// call the callback function if it has been defined
|
|
65
|
+
if (_onSetLanguageTag !== undefined) {
|
|
66
|
+
_onSetLanguageTag(languageTag())
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set the `onSetLanguageTag()` callback function.
|
|
72
|
+
*
|
|
73
|
+
* The function can be used to trigger client-side side-effects such as
|
|
74
|
+
* making a new request to the server with the updated language tag,
|
|
75
|
+
* or re-rendering the UI on the client (SPA apps).
|
|
76
|
+
*
|
|
77
|
+
* - Don't use this function on the server (!).
|
|
78
|
+
* Triggering a side-effect is only useful on the client because a server-side
|
|
79
|
+
* environment doesn't need to re-render the UI.
|
|
80
|
+
*
|
|
81
|
+
* - The `onSetLanguageTag()` callback can only be defined once to avoid unexpected behavior.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* // if you use inlang paraglide on the server, make sure
|
|
85
|
+
* // to not call `onSetLanguageTag()` on the server
|
|
86
|
+
* if (isServer === false) {
|
|
87
|
+
* onSetLanguageTag((tag) => {
|
|
88
|
+
* // (for example) make a new request to the
|
|
89
|
+
* // server with the updated language tag
|
|
90
|
+
* window.location.href = `/${tag}/${window.location.pathname}`
|
|
91
|
+
* })
|
|
92
|
+
* }
|
|
93
|
+
*
|
|
94
|
+
* @param {(languageTag: AvailableLanguageTag) => void} fn
|
|
95
|
+
*/
|
|
96
|
+
export const onSetLanguageTag = (fn) => {
|
|
97
|
+
if (_onSetLanguageTag !== undefined) {
|
|
98
|
+
throw new Error("@inlang/paraglide-js: The `onSetLanguageTag()` callback has already been defined.\n\nThe `onSetLanguageTag()` callback can only be defined once to avoid unexpected behavior.\n\n 1) Try searching for `onSetLanguageTag()` in your codebase for potential duplicated.\n 2) It might be that your framework is calling `onSetLanguageTag()` multiple times. Try to move the `onSetLanguageTag()` out of the rendering scope like a React component.")
|
|
99
|
+
}
|
|
100
|
+
_onSetLanguageTag = fn
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ------ TYPES ------
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A language tag that is available in the project.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* setLanguageTag(request.languageTag as AvailableLanguageTag)
|
|
110
|
+
*
|
|
111
|
+
* @typedef {typeof availableLanguageTags[number]} AvailableLanguageTag
|
|
112
|
+
*/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
import { languageTag } from "./runtime.js"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This message has been compiled by [inlang paraglide](https://inlang.com/marketplace/library.inlang.paraglideJs).
|
|
7
|
+
*
|
|
8
|
+
* - Don't edit the message manually. Use the [inlang ide extension](https://inlang.com/marketplace/app.inlang.ideExtension)
|
|
9
|
+
* or the [web editor](https://inlang.com/marketplace/app.inlang.editor) to edit the message.
|
|
10
|
+
*
|
|
11
|
+
* - The params are NonNullable<unknown> because inlang can't know the value type of a param (yet).
|
|
12
|
+
*
|
|
13
|
+
* @param {{ languageTag: NonNullable<unknown> }} params
|
|
14
|
+
* @returns {string}
|
|
15
|
+
*/
|
|
16
|
+
export const currentLanguageTag = (params) => {
|
|
17
|
+
const variants = {
|
|
18
|
+
"en": `The current language tag is "${params.languageTag}".`,
|
|
19
|
+
"de": `Der aktuelle Sprachtag ist "${params.languageTag}".`
|
|
20
|
+
}
|
|
21
|
+
return variants[languageTag()] ?? "currentLanguageTag"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This message has been compiled by [inlang paraglide](https://inlang.com/marketplace/library.inlang.paraglideJs).
|
|
27
|
+
*
|
|
28
|
+
* - Don't edit the message manually. Use the [inlang ide extension](https://inlang.com/marketplace/app.inlang.ideExtension)
|
|
29
|
+
* or the [web editor](https://inlang.com/marketplace/app.inlang.editor) to edit the message.
|
|
30
|
+
*
|
|
31
|
+
* - The params are NonNullable<unknown> because inlang can't know the value type of a param (yet).
|
|
32
|
+
*
|
|
33
|
+
* @param {{ name: NonNullable<unknown>, count: NonNullable<unknown> }} params
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
export const greeting = (params) => {
|
|
37
|
+
const variants = {
|
|
38
|
+
"en": `Welcome ${params.name}! You have ${params.count} messages.`,
|
|
39
|
+
"de": `Hallo ${params.name}! Du hast ${params.count} Nachrichten.`
|
|
40
|
+
}
|
|
41
|
+
return variants[languageTag()] ?? "greeting"
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project's source language tag.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* if (newlySelectedLanguageTag === sourceLanguageTag){
|
|
6
|
+
* // do nothing as the source language tag is the default language
|
|
7
|
+
* return
|
|
8
|
+
* }
|
|
9
|
+
*/
|
|
10
|
+
export const sourceLanguageTag: "en";
|
|
11
|
+
/**
|
|
12
|
+
* The project's available language tags.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* if (availableLanguageTags.includes(userSelectedLanguageTag) === false){
|
|
16
|
+
* throw new Error("Language tag not available")
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
export const availableLanguageTags: readonly ["en", "de"];
|
|
20
|
+
/**
|
|
21
|
+
* Get the current language tag.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* if (languageTag() === "de"){
|
|
25
|
+
* console.log("Germany 🇩🇪")
|
|
26
|
+
* } else if (languageTag() === "nl"){
|
|
27
|
+
* console.log("Netherlands 🇳🇱")
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* @type {() => AvailableLanguageTag}
|
|
31
|
+
*/
|
|
32
|
+
export let languageTag: () => AvailableLanguageTag;
|
|
33
|
+
export function setLanguageTag(tag: AvailableLanguageTag | (() => AvailableLanguageTag)): void;
|
|
34
|
+
export function onSetLanguageTag(fn: (languageTag: any) => void): void;
|
|
35
|
+
/**
|
|
36
|
+
* A language tag that is available in the project.
|
|
37
|
+
*/
|
|
38
|
+
export type AvailableLanguageTag = (typeof availableLanguageTags)[number];
|