Skip to content

Commit 2dcb4f0

Browse files
update to paraglide js v2 (#461)
* update to paraglide js v2 * add changeset * Update .changeset/eighty-parrots-grab.md Co-authored-by: Manuel <[email protected]> * Update packages/addons/paraglide/index.ts * fix: response already read opral/inlang-paraglide-js#448 * update used node version * update @vitest/ui * use node v20 for testing --------- Co-authored-by: Manuel <[email protected]> Co-authored-by: Manuel <[email protected]>
1 parent 61e7859 commit 2dcb4f0

File tree

7 files changed

+101
-144
lines changed

7 files changed

+101
-144
lines changed

.changeset/eighty-parrots-grab.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sv': patch
3+
---
4+
5+
Updates the Paraglide JS addon to setup v2 of Paraglide JS.

.github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- uses: pnpm/action-setup@v4
2323
- uses: actions/setup-node@v4
2424
with:
25-
node-version: 18
25+
node-version: 20
2626
cache: pnpm
2727
- run: pnpm install --frozen-lockfile
2828
- run: pnpm lint
@@ -33,7 +33,7 @@ jobs:
3333
- uses: pnpm/action-setup@v4
3434
- uses: actions/setup-node@v4
3535
with:
36-
node-version: 18
36+
node-version: 20
3737
cache: pnpm
3838
- run: pnpm install --frozen-lockfile
3939
- run: pnpm build
@@ -48,7 +48,7 @@ jobs:
4848
- uses: pnpm/action-setup@v4
4949
- uses: actions/setup-node@v4
5050
with:
51-
node-version: 18
51+
node-version: 20
5252
cache: pnpm
5353
- run: pnpm install --frozen-lockfile
5454
- run: pnpm exec playwright install chromium

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Setup Node.js
2727
uses: actions/setup-node@v4
2828
with:
29-
node-version: 18.x
29+
node-version: 20
3030
cache: pnpm
3131

3232
- name: Install

.github/workflows/update-template-repo.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Node.js
1818
uses: actions/setup-node@v4
1919
with:
20-
node-version: 18.x
20+
node-version: 20
2121
cache: pnpm
2222

2323
- name: Install

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"@sveltejs/eslint-config": "^8.1.0",
2626
"@svitejs/changesets-changelog-github-compact": "^1.2.0",
2727
"@types/node": "^22.10.2",
28-
"@vitest/ui": "^3.0.3",
28+
"@vitest/ui": "^3.0.5",
2929
"eslint": "^9.17.0",
3030
"magic-string": "^0.30.15",
3131
"prettier": "^3.4.2",

packages/addons/paraglide/index.ts

+39-101
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import MagicString from 'magic-string';
2-
import { colors, dedent, defineAddon, defineAddonOptions, log, utils } from '@sveltejs/cli-core';
2+
import { colors, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core';
33
import {
44
array,
55
common,
@@ -17,24 +17,19 @@ import { addToDemoPage } from '../common.ts';
1717
const DEFAULT_INLANG_PROJECT = {
1818
$schema: 'https://inlang.com/schema/project-settings',
1919
modules: [
20-
'https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js',
21-
'https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js',
22-
'https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js',
23-
'https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js',
24-
'https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-valid-js-identifier@1/dist/index.js',
25-
'https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@2/dist/index.js',
26-
'https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@0/dist/index.js'
20+
'https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js',
21+
'https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js'
2722
],
2823
'plugin.inlang.messageFormat': {
29-
pathPattern: './messages/{languageTag}.json'
24+
pathPattern: './messages/{locale}.json'
3025
}
3126
};
3227

3328
const options = defineAddonOptions({
3429
availableLanguageTags: {
3530
question: `Which languages would you like to support? ${colors.gray('(e.g. en,de-ch)')}`,
3631
type: 'string',
37-
default: 'en',
32+
default: 'en, es',
3833
validate(input) {
3934
const { invalidLanguageTags, validLanguageTags } = parseLanguageTagInput(input);
4035

@@ -62,18 +57,18 @@ const options = defineAddonOptions({
6257
export default defineAddon({
6358
id: 'paraglide',
6459
shortDescription: 'i18n',
65-
homepage: 'https://inlang.com',
60+
homepage: 'https://inlang.com/m/gerre34r/library-inlang-paraglideJs',
6661
options,
6762
setup: ({ kit, unsupported }) => {
6863
if (!kit) unsupported('Requires SvelteKit');
6964
},
70-
run: ({ sv, options, typescript, kit, dependencyVersion }) => {
65+
run: ({ sv, options, typescript, kit }) => {
7166
const ext = typescript ? 'ts' : 'js';
7267
if (!kit) throw new Error('SvelteKit is required');
7368

7469
const paraglideOutDir = 'src/lib/paraglide';
7570

76-
sv.dependency('@inlang/paraglide-sveltekit', '^0.15.5');
71+
sv.dependency('@inlang/paraglide-js', '^2.0.0');
7772

7873
sv.file('project.inlang/settings.json', (content) => {
7974
if (content) return content;
@@ -84,10 +79,10 @@ export default defineAddon({
8479
data[key] = DEFAULT_INLANG_PROJECT[key as keyof typeof DEFAULT_INLANG_PROJECT];
8580
}
8681
const { validLanguageTags } = parseLanguageTagInput(options.availableLanguageTags);
87-
const sourceLanguageTag = validLanguageTags[0];
82+
const baseLocale = validLanguageTags[0];
8883

89-
data.sourceLanguageTag = sourceLanguageTag;
90-
data.languageTags = validLanguageTags;
84+
data.baseLocale = baseLocale;
85+
data.locales = validLanguageTags;
9186

9287
return generateCode();
9388
});
@@ -96,8 +91,8 @@ export default defineAddon({
9691
sv.file(`vite.config.${ext}`, (content) => {
9792
const { ast, generateCode } = parseScript(content);
9893

99-
const vitePluginName = 'paraglide';
100-
imports.addNamed(ast, '@inlang/paraglide-sveltekit/vite', { paraglide: vitePluginName });
94+
const vitePluginName = 'paraglideVitePlugin';
95+
imports.addNamed(ast, '@inlang/paraglide-js', { paraglideVitePlugin: vitePluginName });
10196

10297
const { value: rootObject } = exports.defaultExport(ast, functions.call('defineConfig', []));
10398
const param1 = functions.argumentByIndex(rootObject, 0, object.createEmpty());
@@ -114,31 +109,17 @@ export default defineAddon({
114109
return generateCode();
115110
});
116111

117-
// src/lib/i18n file
118-
sv.file(`src/lib/i18n.${ext}`, (content) => {
119-
const { ast, generateCode } = parseScript(content);
120-
121-
imports.addNamed(ast, '@inlang/paraglide-sveltekit', { createI18n: 'createI18n' });
122-
imports.addDefault(ast, '$lib/paraglide/runtime', '* as runtime');
123-
124-
const createI18nExpression = common.expressionFromString('createI18n(runtime)');
125-
const i18n = variables.declaration(ast, 'const', 'i18n', createI18nExpression);
126-
127-
const existingExport = exports.namedExport(ast, 'i18n', i18n);
128-
if (existingExport.declaration !== i18n) {
129-
log.warn('Setting up $lib/i18n failed because it already exports an i18n function');
130-
}
131-
132-
return generateCode();
133-
});
134-
135112
// reroute hook
136113
sv.file(`src/hooks.${ext}`, (content) => {
137114
const { ast, generateCode } = parseScript(content);
138115

139-
imports.addNamed(ast, '$lib/i18n', { i18n: 'i18n' });
116+
imports.addNamed(ast, '$lib/paraglide/runtime', {
117+
deLocalizeUrl: 'deLocalizeUrl'
118+
});
140119

141-
const expression = common.expressionFromString('i18n.reroute()');
120+
const expression = common.expressionFromString(
121+
'(request) => deLocalizeUrl(request.url).pathname'
122+
);
142123
const rerouteIdentifier = variables.declaration(ast, 'const', 'reroute', expression);
143124

144125
const existingExport = exports.namedExport(ast, 'reroute', rerouteIdentifier);
@@ -153,41 +134,21 @@ export default defineAddon({
153134
sv.file(`src/hooks.server.${ext}`, (content) => {
154135
const { ast, generateCode } = parseScript(content);
155136

156-
imports.addNamed(ast, '$lib/i18n', { i18n: 'i18n' });
137+
imports.addNamed(ast, '$lib/paraglide/server', {
138+
paraglideMiddleware: 'paraglideMiddleware'
139+
});
157140

158-
const hookHandleContent = 'i18n.handle()';
141+
const hookHandleContent = `({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => {
142+
event.request = request;
143+
return resolve(event, {
144+
transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale)
145+
});
146+
});`;
159147
kitJs.addHooksHandle(ast, typescript, 'handleParaglide', hookHandleContent);
160148

161149
return generateCode();
162150
});
163151

164-
// add the <ParaglideJS> component to the layout
165-
sv.file(`${kit.routesDirectory}/+layout.svelte`, (content) => {
166-
const { script, template, generateCode } = parseSvelte(content, { typescript });
167-
168-
const paraglideComponentName = 'ParaglideJS';
169-
imports.addNamed(script.ast, '@inlang/paraglide-sveltekit', {
170-
[paraglideComponentName]: paraglideComponentName
171-
});
172-
imports.addNamed(script.ast, '$lib/i18n', { i18n: 'i18n' });
173-
174-
if (template.source.length === 0) {
175-
const svelteVersion = dependencyVersion('svelte');
176-
if (!svelteVersion) throw new Error('Failed to determine svelte version');
177-
178-
html.addSlot(script.ast, template.ast, svelteVersion);
179-
}
180-
181-
const templateCode = new MagicString(template.generateCode());
182-
if (!templateCode.original.includes('<ParaglideJS')) {
183-
templateCode.indent();
184-
templateCode.prepend('<ParaglideJS {i18n}>\n');
185-
templateCode.append('\n</ParaglideJS>');
186-
}
187-
188-
return generateCode({ script: script.generateCode(), template: templateCode.toString() });
189-
});
190-
191152
// add the text-direction and lang attribute placeholders to app.html
192153
sv.file('src/app.html', (content) => {
193154
const { ast, generateCode } = parseHtml(content);
@@ -204,8 +165,7 @@ export default defineAddon({
204165
}
205166
htmlNode.attribs = {
206167
...htmlNode.attribs,
207-
lang: '%paraglide.lang%',
208-
dir: '%paraglide.textDirection%'
168+
lang: '%paraglide.lang%'
209169
};
210170

211171
return generateCode();
@@ -229,36 +189,14 @@ export default defineAddon({
229189
sv.file(`${kit.routesDirectory}/demo/paraglide/+page.svelte`, (content) => {
230190
const { script, template, generateCode } = parseSvelte(content, { typescript });
231191

232-
imports.addDefault(script.ast, '$lib/paraglide/messages.js', '* as m');
192+
imports.addNamed(script.ast, '$lib/paraglide/messages.js', { m: 'm' });
233193
imports.addNamed(script.ast, '$app/navigation', { goto: 'goto' });
234194
imports.addNamed(script.ast, '$app/state', { page: 'page' });
235-
imports.addNamed(script.ast, '$lib/i18n', { i18n: 'i18n' });
236-
if (typescript) {
237-
imports.addNamed(
238-
script.ast,
239-
'$lib/paraglide/runtime',
240-
{ AvailableLanguageTag: 'AvailableLanguageTag' },
241-
true
242-
);
243-
}
244-
245-
const [ts] = utils.createPrinter(typescript);
195+
imports.addNamed(script.ast, '$lib/paraglide/runtime', {
196+
setLocale: 'setLocale'
197+
});
246198

247199
const scriptCode = new MagicString(script.generateCode());
248-
if (!scriptCode.original.includes('function switchToLanguage')) {
249-
scriptCode.trim();
250-
scriptCode.append('\n\n');
251-
scriptCode.append(dedent`
252-
${ts('', '/**')}
253-
${ts('', '* @param {import("$lib/paraglide/runtime").AvailableLanguageTag} newLanguage')}
254-
${ts('', '*/')}
255-
function switchToLanguage(newLanguage${ts(': AvailableLanguageTag')}) {
256-
const canonicalPath = i18n.route(page.url.pathname);
257-
const localisedPath = i18n.resolveRoute(canonicalPath, newLanguage);
258-
goto(localisedPath);
259-
}
260-
`);
261-
}
262200

263201
const templateCode = new MagicString(template.source);
264202

@@ -271,11 +209,15 @@ export default defineAddon({
271209
const links = validLanguageTags
272210
.map(
273211
(x) =>
274-
`${templateCode.getIndentString()}<button onclick={() => switchToLanguage('${x}')}>${x}</button>`
212+
`${templateCode.getIndentString()}<button onclick={() => setLocale('${x}')}>${x}</button>`
275213
)
276214
.join('\n');
277215
templateCode.append(`<div>\n${links}\n</div>`);
278216

217+
templateCode.append(
218+
'<p>\nIf you use VSCode, install the <a href="https://marketplace.visualstudio.com/items?itemName=inlang.vs-code-extension" target="_blank">Sherlock i18n extension</a> for a better i18n experience.\n</p>'
219+
);
220+
279221
return generateCode({ script: scriptCode.toString(), template: templateCode.toString() });
280222
});
281223
}
@@ -286,17 +228,13 @@ export default defineAddon({
286228
const { data, generateCode } = parseJson(content);
287229
data['$schema'] = 'https://inlang.com/schema/inlang-message-format';
288230
data.hello_world = `Hello, {name} from ${languageTag}!`;
289-
290231
return generateCode();
291232
});
292233
}
293234
},
294235

295236
nextSteps: ({ highlighter }) => {
296-
const steps = [
297-
`Edit your messages in ${highlighter.path('messages/en.json')}`,
298-
'Consider installing the Sherlock IDE Extension'
299-
];
237+
const steps = [`Edit your messages in ${highlighter.path('messages/en.json')}`];
300238
if (options.demo) {
301239
steps.push(`Visit ${highlighter.route('/demo/paraglide')} route to view the demo`);
302240
}

0 commit comments

Comments
 (0)