next-intl 4.13.7 → 4.14.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/dist/cjs/development/BuiltInPoCodec-B6w-kmTx.cjs +34 -0
- package/dist/cjs/development/plugin.cjs +1607 -8
- package/dist/esm/development/extractor/catalog/CatalogManager.js +3 -2
- package/dist/esm/development/extractor/catalog/CatalogPersister.js +21 -2
- package/dist/esm/development/extractor/catalogLoader.js +38 -16
- package/dist/esm/development/extractor/extractor/MessageExtractor.js +8 -1
- package/dist/esm/development/extractor/format/ExtractorCodec.js +3 -0
- package/dist/esm/development/extractor/format/codecs/BuiltInPoCodec.js +22 -0
- package/dist/esm/development/extractor/format/index.js +19 -4
- package/dist/esm/development/extractor/utils.js +8 -27
- package/dist/esm/development/plugin/getNextConfig.js +6 -2
- package/dist/esm/production/extractor/catalog/CatalogManager.js +1 -1
- package/dist/esm/production/extractor/catalog/CatalogPersister.js +1 -1
- package/dist/esm/production/extractor/catalogLoader.js +1 -1
- package/dist/esm/production/extractor/extractor/MessageExtractor.js +1 -1
- package/dist/esm/production/extractor/format/codecs/BuiltInPoCodec.js +1 -0
- package/dist/esm/production/extractor/format/index.js +1 -1
- package/dist/esm/production/extractor/utils.js +1 -1
- package/dist/esm/production/plugin/getNextConfig.js +1 -1
- package/dist/types/extractor/catalog/CatalogManager.d.ts +1 -1
- package/dist/types/extractor/catalog/CatalogPersister.d.ts +2 -0
- package/dist/types/extractor/extractor/MessageExtractor.d.ts +2 -2
- package/dist/types/extractor/format/ExtractorCodec.d.ts +11 -27
- package/dist/types/extractor/format/codecs/fixtures/POCodecLegacy.d.ts +50 -0
- package/dist/types/extractor/format/index.d.ts +2 -2
- package/dist/types/extractor/types.d.ts +9 -21
- package/dist/types/extractor/utils.d.ts +1 -1
- package/package.json +9 -12
- package/dist/cjs/development/ExtractorCodec-D9Tw618d.cjs +0 -7
- package/dist/cjs/development/JSONCodec-CzA8ubPy.cjs +0 -55
- package/dist/cjs/development/POCodec-CWGHK-Gp.cjs +0 -111
- package/dist/cjs/development/plugin-DlFYUFWh.cjs +0 -1613
- package/dist/esm/development/extractor/format/codecs/JSONCodec.js +0 -47
- package/dist/esm/development/extractor/format/codecs/POCodec.js +0 -99
- package/dist/esm/production/extractor/format/codecs/JSONCodec.js +0 -1
- package/dist/esm/production/extractor/format/codecs/POCodec.js +0 -1
- package/dist/types/extractor/format/codecs/POCodec.d.ts +0 -2
- package/dist/types/extractor/format/codecs/fixtures/POCodecSourceMessageKey.d.ts +0 -2
- /package/dist/types/extractor/format/codecs/{JSONCodec.d.ts → BuiltInPoCodec.d.ts} +0 -0
|
@@ -23,7 +23,7 @@ class CatalogManager {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Reverse index for rebuilding aggregated messages without scanning all files.
|
|
26
|
-
* Contains the same `
|
|
26
|
+
* Contains the same `SourceExtractedMessage` arrays as `sourceMessagesByFile` and is
|
|
27
27
|
* kept in sync with it.
|
|
28
28
|
*/
|
|
29
29
|
sourceMessagesById = new Map();
|
|
@@ -74,7 +74,8 @@ class CatalogManager {
|
|
|
74
74
|
this.persister = new CatalogPersister({
|
|
75
75
|
messagesPath: this.config.extract.path,
|
|
76
76
|
codec: await this.getCodec(),
|
|
77
|
-
extension: getFormatExtension(this.config.messages.format)
|
|
77
|
+
extension: getFormatExtension(this.config.messages.format),
|
|
78
|
+
sourceLocale: this.config.extract.sourceLocale
|
|
78
79
|
});
|
|
79
80
|
return this.persister;
|
|
80
81
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { warnAboutMissingReferences } from '../utils.js';
|
|
3
4
|
|
|
4
5
|
class CatalogPersister {
|
|
5
6
|
constructor(params) {
|
|
6
7
|
this.messagesPath = params.messagesPath;
|
|
7
8
|
this.codec = params.codec;
|
|
8
9
|
this.extension = params.extension;
|
|
10
|
+
this.sourceLocale = params.sourceLocale;
|
|
9
11
|
}
|
|
10
12
|
getFileName(locale) {
|
|
11
13
|
return locale + this.extension;
|
|
@@ -27,9 +29,12 @@ class CatalogPersister {
|
|
|
27
29
|
});
|
|
28
30
|
}
|
|
29
31
|
try {
|
|
30
|
-
|
|
31
|
-
locale
|
|
32
|
+
const messages = this.codec.decode(content, {
|
|
33
|
+
locale,
|
|
34
|
+
sourceLocale: this.sourceLocale
|
|
32
35
|
});
|
|
36
|
+
assertNoArrayMessages(messages);
|
|
37
|
+
return messages;
|
|
33
38
|
} catch (error) {
|
|
34
39
|
throw new Error(`Error while decoding ${this.getFileName(locale)}:\n> ${error}`, {
|
|
35
40
|
cause: error
|
|
@@ -38,6 +43,7 @@ class CatalogPersister {
|
|
|
38
43
|
}
|
|
39
44
|
async write(messages, context) {
|
|
40
45
|
const filePath = this.getFilePath(context.locale);
|
|
46
|
+
warnAboutMissingReferences(messages);
|
|
41
47
|
const content = this.codec.encode(messages, context);
|
|
42
48
|
try {
|
|
43
49
|
const outputDir = path.dirname(filePath);
|
|
@@ -60,4 +66,17 @@ class CatalogPersister {
|
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
|
|
69
|
+
// The codecs read arrays fine (numeric path segments), but the runtime only
|
|
70
|
+
// supports strings — enforced here where the runtime's rules apply.
|
|
71
|
+
function assertNoArrayMessages(messages) {
|
|
72
|
+
for (const message of messages) {
|
|
73
|
+
const segments = message.segments;
|
|
74
|
+
if (!Array.isArray(segments)) continue;
|
|
75
|
+
const firstIndex = segments.findIndex(segment => typeof segment === 'number');
|
|
76
|
+
if (firstIndex === -1) continue;
|
|
77
|
+
const arrayPath = segments.slice(0, firstIndex).join('.');
|
|
78
|
+
throw new Error(`Message at \`${arrayPath}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
export { CatalogPersister as default };
|
|
@@ -6,6 +6,7 @@ import { setNestedProperty } from './utils.js';
|
|
|
6
6
|
// The module scope is safe for some caching, but Next.js can
|
|
7
7
|
// create multiple loader instances so don't expect a singleton.
|
|
8
8
|
let cachedCodec = null;
|
|
9
|
+
let hasWarnedAboutToJSONString = false;
|
|
9
10
|
const messageCacheByCatalog = new Map();
|
|
10
11
|
function getMessageCache(catalogId) {
|
|
11
12
|
let cache = messageCacheByCatalog.get(catalogId);
|
|
@@ -34,26 +35,52 @@ function catalogLoader(source) {
|
|
|
34
35
|
const callback = this.async();
|
|
35
36
|
const extension = getFormatExtension(options.messages.format);
|
|
36
37
|
getCodec(options, this.rootContext).then(codec => {
|
|
38
|
+
warnIfToJSONStringProvided(codec);
|
|
37
39
|
const locale = path.basename(this.resourcePath, extension);
|
|
38
|
-
|
|
40
|
+
const decoded = codec.decode(source, {
|
|
41
|
+
locale,
|
|
42
|
+
sourceLocale: options.messages.sourceLocale
|
|
43
|
+
});
|
|
44
|
+
let messages;
|
|
39
45
|
if (options.messages.precompile) {
|
|
40
|
-
const decoded = codec.decode(source, {
|
|
41
|
-
locale
|
|
42
|
-
});
|
|
43
46
|
const cache = getMessageCache(this.resourcePath);
|
|
44
|
-
|
|
45
|
-
outputString = JSON.stringify(precompiled);
|
|
47
|
+
messages = precompileMessages(decoded, cache);
|
|
46
48
|
} else {
|
|
47
|
-
|
|
48
|
-
locale
|
|
49
|
-
});
|
|
49
|
+
messages = buildMessages(decoded);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// https://v8.dev/blog/cost-of-javascript-2019#json
|
|
53
|
-
const result = `export default JSON.parse(${JSON.stringify(
|
|
53
|
+
const result = `export default JSON.parse(${JSON.stringify(JSON.stringify(messages))});`;
|
|
54
54
|
callback(null, result);
|
|
55
55
|
}).catch(callback);
|
|
56
56
|
}
|
|
57
|
+
function warnIfToJSONStringProvided(codec) {
|
|
58
|
+
if (hasWarnedAboutToJSONString || !codec.toJSONString) return;
|
|
59
|
+
hasWarnedAboutToJSONString = true;
|
|
60
|
+
console.warn('`toJSONString` is deprecated and no longer needed, please remove it.');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Builds a nested messages object from decoded messages, mirroring the shape
|
|
65
|
+
* produced by `precompileMessages` but keeping the raw message strings.
|
|
66
|
+
*/
|
|
67
|
+
function buildMessages(messages) {
|
|
68
|
+
const result = Object.create(null);
|
|
69
|
+
for (const message of messages) {
|
|
70
|
+
assertStringMessage(message);
|
|
71
|
+
setNestedProperty(result, message.id, message.message);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function assertStringMessage(message) {
|
|
76
|
+
const messageValue = message.message;
|
|
77
|
+
if (Array.isArray(messageValue)) {
|
|
78
|
+
throw new Error(`Message at \`${message.id}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);
|
|
79
|
+
}
|
|
80
|
+
if (typeof messageValue === 'object') {
|
|
81
|
+
throw new Error(`Message at \`${message.id}\` resolved to \`${typeof messageValue}\`, but only strings are supported. Use a \`.\` to retrieve nested messages. See https://next-intl.dev/docs/usage/translations#structuring-messages`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
57
84
|
|
|
58
85
|
/**
|
|
59
86
|
* Recursively precompiles all ICU message strings in a messages object
|
|
@@ -64,13 +91,8 @@ function precompileMessages(messages, cache) {
|
|
|
64
91
|
const cacheKeysToEvict = new Set(cache.keys());
|
|
65
92
|
for (const message of messages) {
|
|
66
93
|
cacheKeysToEvict.delete(message.id);
|
|
94
|
+
assertStringMessage(message);
|
|
67
95
|
const messageValue = message.message;
|
|
68
|
-
if (Array.isArray(messageValue)) {
|
|
69
|
-
throw new Error(`Message at \`${message.id}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);
|
|
70
|
-
}
|
|
71
|
-
if (typeof messageValue === 'object') {
|
|
72
|
-
throw new Error(`Message at \`${message.id}\` resolved to \`${typeof messageValue}\`, but only strings are supported. Use a \`.\` to retrieve nested messages. See https://next-intl.dev/docs/usage/translations#structuring-messages`);
|
|
73
|
-
}
|
|
74
96
|
const cachedEntry = cache.get(message.id);
|
|
75
97
|
const hasCacheMatch = cachedEntry?.messageValue === messageValue;
|
|
76
98
|
let compiledMessage;
|
|
@@ -52,7 +52,14 @@ class MessageExtractor {
|
|
|
52
52
|
|
|
53
53
|
// TODO: Improve the typing of @swc/core
|
|
54
54
|
const output = result.output;
|
|
55
|
-
|
|
55
|
+
// The plugin emits a tagged union of extracted messages and
|
|
56
|
+
// `useTranslations` usages; this extractor only consumes the former.
|
|
57
|
+
const messages = JSON.parse(JSON.parse(output).results).filter(item => item.type === 'extracted').map(item => ({
|
|
58
|
+
id: item.id,
|
|
59
|
+
message: item.message,
|
|
60
|
+
description: item.description,
|
|
61
|
+
reference: item.reference
|
|
62
|
+
}));
|
|
56
63
|
const extractionResult = {
|
|
57
64
|
code: result.code,
|
|
58
65
|
map: result.map,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import createPoCodec from '@eloqnt/format-po';
|
|
2
|
+
import { defineCodec } from '../ExtractorCodec.js';
|
|
3
|
+
|
|
4
|
+
// `@eloqnt/format-po`, guarded so catalogs in the previous key-based layout
|
|
5
|
+
// fail loudly. Custom codecs referencing the package directly opt out.
|
|
6
|
+
var BuiltInPoCodec = defineCodec(() => {
|
|
7
|
+
const codec = createPoCodec();
|
|
8
|
+
function assertMigrated(content) {
|
|
9
|
+
if (content.includes('X-Crowdin-SourceKey: msgstr')) {
|
|
10
|
+
throw new Error('This catalog uses the previous PO layout of next-intl, which stored message keys in `msgid`. The PO format now follows the gettext convention: the source text in `msgid` and the message key in `msgctxt`. Please migrate your catalogs, or use a custom codec to keep the previous layout: https://github.com/amannn/next-intl/pull/2393');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
...codec,
|
|
15
|
+
decode(content, context) {
|
|
16
|
+
assertMigrated(content);
|
|
17
|
+
return codec.decode(content, context);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export { BuiltInPoCodec as default };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
1
2
|
import path from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
2
4
|
import { throwError } from '../../plugin/utils.js';
|
|
3
5
|
|
|
4
6
|
const formats = {
|
|
5
7
|
json: {
|
|
6
|
-
codec: () => import('
|
|
8
|
+
codec: () => import('@eloqnt/format-json'),
|
|
7
9
|
extension: '.json'
|
|
8
10
|
},
|
|
9
11
|
po: {
|
|
10
|
-
codec: () => import('./codecs/
|
|
12
|
+
codec: () => import('./codecs/BuiltInPoCodec.js'),
|
|
11
13
|
extension: '.po'
|
|
12
14
|
}
|
|
13
15
|
};
|
|
@@ -26,10 +28,23 @@ async function resolveCodec(format, projectRoot) {
|
|
|
26
28
|
const factory = (await formats[format].codec()).default;
|
|
27
29
|
return factory();
|
|
28
30
|
} else {
|
|
29
|
-
|
|
31
|
+
// A codec that starts with `.` or a filesystem root is a file path;
|
|
32
|
+
// everything else reads as a package specifier, mirroring how imports
|
|
33
|
+
// distinguish the two. Packages resolve from the project's dependencies.
|
|
34
|
+
const isPath = format.codec.startsWith('.') || path.isAbsolute(format.codec);
|
|
35
|
+
let resolvedPath;
|
|
36
|
+
if (isPath) {
|
|
37
|
+
resolvedPath = path.resolve(projectRoot, format.codec);
|
|
38
|
+
} else {
|
|
39
|
+
try {
|
|
40
|
+
resolvedPath = createRequire(path.join(projectRoot, 'noop.js')).resolve(format.codec);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throwError(`Could not resolve the codec package "${format.codec}". Is it installed?\n${error}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
30
45
|
let module;
|
|
31
46
|
try {
|
|
32
|
-
module = await import(resolvedPath);
|
|
47
|
+
module = await import(pathToFileURL(resolvedPath).href);
|
|
33
48
|
} catch (error) {
|
|
34
49
|
throwError(`Could not load codec from "${resolvedPath}".\n${error}`);
|
|
35
50
|
}
|
|
@@ -35,34 +35,15 @@ function setNestedProperty(obj, keyPath, value) {
|
|
|
35
35
|
}
|
|
36
36
|
current[keys[keys.length - 1]] = value;
|
|
37
37
|
}
|
|
38
|
-
function
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const refA = messageA.references[0];
|
|
42
|
-
const refB = messageB.references[0];
|
|
43
|
-
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
45
|
-
if (refA == null) {
|
|
46
|
-
warnAboutMissingReference(messageA.id, warnedMissingReferenceIds);
|
|
47
|
-
}
|
|
38
|
+
function warnAboutMissingReferences(messages) {
|
|
39
|
+
const warnedIds = new Set();
|
|
40
|
+
for (const message of messages) {
|
|
48
41
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
49
|
-
if (
|
|
50
|
-
|
|
42
|
+
if (message.references[0] == null && !warnedIds.has(message.id)) {
|
|
43
|
+
warnedIds.add(message.id);
|
|
44
|
+
warn(`Missing file reference for extracted message: ${message.id}`);
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
54
|
-
if (refA == null || refB == null) {
|
|
55
|
-
return 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Sort by path, then line. Same path+line: preserve original order
|
|
59
|
-
return compareReferences(refA, refB);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
function warnAboutMissingReference(id, warnedMissingReferenceIds) {
|
|
63
|
-
if (warnedMissingReferenceIds.has(id)) return;
|
|
64
|
-
warnedMissingReferenceIds.add(id);
|
|
65
|
-
warn(`Missing file reference for extracted message: ${id}`);
|
|
46
|
+
}
|
|
66
47
|
}
|
|
67
48
|
function localeCompare(a, b) {
|
|
68
49
|
return a.localeCompare(b, 'en');
|
|
@@ -76,4 +57,4 @@ function getDefaultProjectRoot() {
|
|
|
76
57
|
return process.cwd();
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
export { compareReferences, getDefaultProjectRoot,
|
|
60
|
+
export { compareReferences, getDefaultProjectRoot, hasLocalesToExtract, isForbiddenObjectKey, localeCompare, normalizePathToPosix, setNestedProperty, warnAboutMissingReferences };
|
|
@@ -72,7 +72,10 @@ function getNextConfig(pluginConfig, nextConfig, extractorConfig) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
function getCatalogLoaderConfig() {
|
|
75
|
-
const
|
|
75
|
+
const experimental = pluginConfig.experimental;
|
|
76
|
+
const messages = experimental.messages;
|
|
77
|
+
// Normalization enforces a source locale before extraction runs.
|
|
78
|
+
const sourceLocale = messages.sourceLocale ?? (typeof experimental.extract === 'object' ? experimental.extract.sourceLocale : undefined);
|
|
76
79
|
return {
|
|
77
80
|
loader: 'next-intl/extractor/catalogLoader',
|
|
78
81
|
options: {
|
|
@@ -80,7 +83,8 @@ function getNextConfig(pluginConfig, nextConfig, extractorConfig) {
|
|
|
80
83
|
format: messages.format,
|
|
81
84
|
...(messages.precompile !== undefined && {
|
|
82
85
|
precompile: messages.precompile
|
|
83
|
-
})
|
|
86
|
+
}),
|
|
87
|
+
sourceLocale
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"fs/promises";import s from"path";import{resolveCodec as t,getFormatExtension as a}from"../format/index.js";import o from"../source/SourceFileScanner.js";import r from"../source/SourceFileWatcher.js";import{getDefaultProjectRoot as i,localeCompare as c,compareReferences as n}from"../utils.js";import l from"./CatalogLocales.js";import h from"./CatalogPersister.js";import g from"./SaveScheduler.js";class d{static extractorOwnedAggregatorKeys=new Set(["description","id","message","references"]);sourceMessagesByFile=new Map;sourceMessagesById=new Map;messagesById=new Map;translationsByTargetLocale=new Map;lastWriteByLocale=new Map;constructor(e,s){this.config=e,this.saveScheduler=new g(s.saveDebounceMs??50),this.projectRoot=s.projectRoot??i(),this.isDevelopment=s.isDevelopment??!1,this.extractor=s.extractor,this.isDevelopment&&(this.sourceWatcher=new r(this.getSrcPaths(),this.handleFileEvents.bind(this)),this.sourceWatcher.start())}async getCodec(){return this.codec||(this.codec=await t(this.config.messages.format,this.projectRoot)),this.codec}async getPersister(){return this.persister||(this.persister=new h({messagesPath:this.config.extract.path,codec:await this.getCodec(),extension:a(this.config.messages.format)})),this.persister}getCatalogLocales(){if(this.catalogLocales)return this.catalogLocales;{const e=s.join(this.projectRoot,this.config.extract.path);return this.catalogLocales=new l({messagesDir:e,extension:a(this.config.messages.format),locales:this.config.extract.locales,sourceLocale:this.config.extract.sourceLocale}),this.catalogLocales}}async getTargetLocales(){return this.getCatalogLocales().getTargetLocales()}getSrcPaths(){return(Array.isArray(this.config.extract.srcPath)?this.config.extract.srcPath:[this.config.extract.srcPath]).map((e=>s.join(this.projectRoot,e)))}async loadMessages(){const e=await this.loadSourceMessages();if(this.loadCatalogsPromise=this.loadTargetMessages(),await this.loadCatalogsPromise,this.scanCompletePromise=(async()=>{const s=Array.from(await o.getSourceFiles(this.getSrcPaths())).toSorted(c),t=await Promise.all(s.map((async e=>({filePath:e,messages:await this.extractFile(e)}))));for(const{filePath:e,messages:s}of t)s&&this.applyFileMessages(e,s);this.mergeSourceDiskMetadata(e)})(),await this.scanCompletePromise,this.isDevelopment){this.getCatalogLocales().subscribeLocalesChange(this.onLocalesChange)}}async loadSourceMessages(){const e=await this.loadLocaleMessages(this.config.extract.sourceLocale),s=new Map;for(const t of e)s.set(t.id,t);return s}async loadLocaleMessages(e){const s=await this.getPersister(),t=await s.read(e),a=await s.getLastModified(e);return this.lastWriteByLocale.set(e,a),t}async loadTargetMessages(){const e=await this.getTargetLocales();await Promise.all(e.map((e=>this.reloadLocaleCatalog(e))))}async reloadLocaleCatalog(e){const s=await this.loadLocaleMessages(e);if(e===this.config.extract.sourceLocale)for(const e of s){const s=this.messagesById.get(e.id);if(s)for(const t of Object.keys(e))d.extractorOwnedAggregatorKeys.has(t)||(s[t]=e[t])}else{const t=this.translationsByTargetLocale.get(e),a=t&&t.size>0;if(s.length>0){const t=new Map;for(const e of s)t.set(e.id,e);this.translationsByTargetLocale.set(e,t)}else if(a);else{const s=new Map;this.translationsByTargetLocale.set(e,s)}}}mergeSourceDiskMetadata(e){for(const[s,t]of e){const e=this.messagesById.get(s);if(e)for(const s of Object.keys(t))d.extractorOwnedAggregatorKeys.has(s)||null!=e[s]||(e[s]=t[s])}}async processFile(e){const s=await this.extractFile(e);return!!s&&this.applyFileMessages(e,s)}async extractFile(s){let t=[];try{const a=await e.readFile(s,"utf8");let o;try{o=await this.extractor.extract(s,a)}catch{return}t=o.messages}catch(e){if("ENOENT"!==e.code)throw e}return t}applyFileMessages(e,s){const t=this.sourceMessagesByFile.get(e),a=this.groupSourceMessagesById(s),o=new Set([...t?.keys()??[],...a.keys()]);a.size>0?this.sourceMessagesByFile.set(e,a):this.sourceMessagesByFile.delete(e);for(const s of o){const t=this.sourceMessagesById.get(s);t&&(t.delete(e),0===t.size&&this.sourceMessagesById.delete(s));const o=a.get(s);if(o){let t=this.sourceMessagesById.get(s);t||(t=new Map,this.sourceMessagesById.set(s,t)),t.set(e,o)}this.rebuildMessageById(s)}return this.haveMessagesChangedForFile(t,a)}groupSourceMessagesById(e){const s=new Map;for(const t of e){const e=s.get(t.id);e?e.push(t):s.set(t.id,[t])}return s}rebuildMessageById(e){const s=Array.from(this.sourceMessagesById.get(e)?.values()??[]).flat();if(0===s.length)return void this.messagesById.delete(e);const t=this.messagesById.get(e),a={description:this.mergeDescriptions(s),id:e,message:s[0].message,references:s.map((e=>e.reference)).sort(n)};if(t)for(const e of Object.keys(t))d.extractorOwnedAggregatorKeys.has(e)||null!=a[e]||(a[e]=t[e]);this.messagesById.set(e,a)}mergeDescriptions(e){const s=e.toSorted(((e,s)=>n(e.reference,s.reference))),t=[];for(const e of s){const{description:s}=e;null==s||t.includes(s)||t.push(s)}return t}haveMessagesChangedForFile(e,s){if(!e)return s.size>0;if(e.size!==s.size)return!0;for(const[t,a]of e){const e=s.get(t);if(!e)return!0;if(!this.areSourceMessageArraysEqual(a,e))return!0}return!1}areSourceMessageArraysEqual(e,s){return e.length===s.length&&e.every(((e,t)=>this.areSourceMessagesEqual(e,s[t])))}areSourceMessagesEqual(e,s){return e.id===s.id&&e.message===s.message&&e.description===s.description&&e.reference.path===s.reference.path&&e.reference.line===s.reference.line}async save(){return this.saveScheduler.schedule((()=>this.saveImpl()))}async saveImpl(){await this.saveLocale(this.config.extract.sourceLocale);const e=await this.getTargetLocales();await Promise.all(e.map((e=>this.saveLocale(e))))}async saveLocale(e){await this.loadCatalogsPromise;const s=Array.from(this.messagesById.values()),t=await this.getPersister(),a=e===this.config.extract.sourceLocale,o=this.lastWriteByLocale.get(e),r=await t.getLastModified(e);r&&o&&r>o&&await this.reloadLocaleCatalog(e);const i=a?this.messagesById:this.translationsByTargetLocale.get(e),c=s.map((e=>{const s=i?.get(e.id);return{...s,id:e.id,description:e.description,references:e.references,message:a?e.message:s?.message??""}}));await t.write(c,{locale:e,sourceMessagesById:this.messagesById});const n=await t.getLastModified(e);this.lastWriteByLocale.set(e,n)}onLocalesChange=async e=>{this.loadCatalogsPromise=Promise.all([this.loadCatalogsPromise,...e.added.map((e=>this.reloadLocaleCatalog(e)))]);for(const s of e.added)await this.saveLocale(s);for(const s of e.removed)this.translationsByTargetLocale.delete(s),this.lastWriteByLocale.delete(s)};async handleFileEvents(e){this.loadCatalogsPromise&&await this.loadCatalogsPromise,this.scanCompletePromise&&await this.scanCompletePromise;let s=!1;const t=await this.sourceWatcher.expandDirectoryDeleteEvents(e,Array.from(this.sourceMessagesByFile.keys()));for(const e of t.toSorted(((e,s)=>c(e.path,s.path)))){const t=await this.processFile(e.path);s||=t}s&&await this.save()}[Symbol.dispose](){this.sourceWatcher?.stop(),this.sourceWatcher=void 0,this.saveScheduler[Symbol.dispose](),this.catalogLocales&&this.isDevelopment&&this.catalogLocales.unsubscribeLocalesChange(this.onLocalesChange)}}export{d as default};
|
|
1
|
+
import e from"fs/promises";import s from"path";import{resolveCodec as t,getFormatExtension as a}from"../format/index.js";import o from"../source/SourceFileScanner.js";import r from"../source/SourceFileWatcher.js";import{getDefaultProjectRoot as i,localeCompare as c,compareReferences as n}from"../utils.js";import l from"./CatalogLocales.js";import h from"./CatalogPersister.js";import g from"./SaveScheduler.js";class d{static extractorOwnedAggregatorKeys=new Set(["description","id","message","references"]);sourceMessagesByFile=new Map;sourceMessagesById=new Map;messagesById=new Map;translationsByTargetLocale=new Map;lastWriteByLocale=new Map;constructor(e,s){this.config=e,this.saveScheduler=new g(s.saveDebounceMs??50),this.projectRoot=s.projectRoot??i(),this.isDevelopment=s.isDevelopment??!1,this.extractor=s.extractor,this.isDevelopment&&(this.sourceWatcher=new r(this.getSrcPaths(),this.handleFileEvents.bind(this)),this.sourceWatcher.start())}async getCodec(){return this.codec||(this.codec=await t(this.config.messages.format,this.projectRoot)),this.codec}async getPersister(){return this.persister||(this.persister=new h({messagesPath:this.config.extract.path,codec:await this.getCodec(),extension:a(this.config.messages.format),sourceLocale:this.config.extract.sourceLocale})),this.persister}getCatalogLocales(){if(this.catalogLocales)return this.catalogLocales;{const e=s.join(this.projectRoot,this.config.extract.path);return this.catalogLocales=new l({messagesDir:e,extension:a(this.config.messages.format),locales:this.config.extract.locales,sourceLocale:this.config.extract.sourceLocale}),this.catalogLocales}}async getTargetLocales(){return this.getCatalogLocales().getTargetLocales()}getSrcPaths(){return(Array.isArray(this.config.extract.srcPath)?this.config.extract.srcPath:[this.config.extract.srcPath]).map((e=>s.join(this.projectRoot,e)))}async loadMessages(){const e=await this.loadSourceMessages();if(this.loadCatalogsPromise=this.loadTargetMessages(),await this.loadCatalogsPromise,this.scanCompletePromise=(async()=>{const s=Array.from(await o.getSourceFiles(this.getSrcPaths())).toSorted(c),t=await Promise.all(s.map((async e=>({filePath:e,messages:await this.extractFile(e)}))));for(const{filePath:e,messages:s}of t)s&&this.applyFileMessages(e,s);this.mergeSourceDiskMetadata(e)})(),await this.scanCompletePromise,this.isDevelopment){this.getCatalogLocales().subscribeLocalesChange(this.onLocalesChange)}}async loadSourceMessages(){const e=await this.loadLocaleMessages(this.config.extract.sourceLocale),s=new Map;for(const t of e)s.set(t.id,t);return s}async loadLocaleMessages(e){const s=await this.getPersister(),t=await s.read(e),a=await s.getLastModified(e);return this.lastWriteByLocale.set(e,a),t}async loadTargetMessages(){const e=await this.getTargetLocales();await Promise.all(e.map((e=>this.reloadLocaleCatalog(e))))}async reloadLocaleCatalog(e){const s=await this.loadLocaleMessages(e);if(e===this.config.extract.sourceLocale)for(const e of s){const s=this.messagesById.get(e.id);if(s)for(const t of Object.keys(e))d.extractorOwnedAggregatorKeys.has(t)||(s[t]=e[t])}else{const t=this.translationsByTargetLocale.get(e),a=t&&t.size>0;if(s.length>0){const t=new Map;for(const e of s)t.set(e.id,e);this.translationsByTargetLocale.set(e,t)}else if(a);else{const s=new Map;this.translationsByTargetLocale.set(e,s)}}}mergeSourceDiskMetadata(e){for(const[s,t]of e){const e=this.messagesById.get(s);if(e)for(const s of Object.keys(t))d.extractorOwnedAggregatorKeys.has(s)||null!=e[s]||(e[s]=t[s])}}async processFile(e){const s=await this.extractFile(e);return!!s&&this.applyFileMessages(e,s)}async extractFile(s){let t=[];try{const a=await e.readFile(s,"utf8");let o;try{o=await this.extractor.extract(s,a)}catch{return}t=o.messages}catch(e){if("ENOENT"!==e.code)throw e}return t}applyFileMessages(e,s){const t=this.sourceMessagesByFile.get(e),a=this.groupSourceMessagesById(s),o=new Set([...t?.keys()??[],...a.keys()]);a.size>0?this.sourceMessagesByFile.set(e,a):this.sourceMessagesByFile.delete(e);for(const s of o){const t=this.sourceMessagesById.get(s);t&&(t.delete(e),0===t.size&&this.sourceMessagesById.delete(s));const o=a.get(s);if(o){let t=this.sourceMessagesById.get(s);t||(t=new Map,this.sourceMessagesById.set(s,t)),t.set(e,o)}this.rebuildMessageById(s)}return this.haveMessagesChangedForFile(t,a)}groupSourceMessagesById(e){const s=new Map;for(const t of e){const e=s.get(t.id);e?e.push(t):s.set(t.id,[t])}return s}rebuildMessageById(e){const s=Array.from(this.sourceMessagesById.get(e)?.values()??[]).flat();if(0===s.length)return void this.messagesById.delete(e);const t=this.messagesById.get(e),a={description:this.mergeDescriptions(s),id:e,message:s[0].message,references:s.map((e=>e.reference)).sort(n)};if(t)for(const e of Object.keys(t))d.extractorOwnedAggregatorKeys.has(e)||null!=a[e]||(a[e]=t[e]);this.messagesById.set(e,a)}mergeDescriptions(e){const s=e.toSorted(((e,s)=>n(e.reference,s.reference))),t=[];for(const e of s){const{description:s}=e;null==s||t.includes(s)||t.push(s)}return t}haveMessagesChangedForFile(e,s){if(!e)return s.size>0;if(e.size!==s.size)return!0;for(const[t,a]of e){const e=s.get(t);if(!e)return!0;if(!this.areSourceMessageArraysEqual(a,e))return!0}return!1}areSourceMessageArraysEqual(e,s){return e.length===s.length&&e.every(((e,t)=>this.areSourceMessagesEqual(e,s[t])))}areSourceMessagesEqual(e,s){return e.id===s.id&&e.message===s.message&&e.description===s.description&&e.reference.path===s.reference.path&&e.reference.line===s.reference.line}async save(){return this.saveScheduler.schedule((()=>this.saveImpl()))}async saveImpl(){await this.saveLocale(this.config.extract.sourceLocale);const e=await this.getTargetLocales();await Promise.all(e.map((e=>this.saveLocale(e))))}async saveLocale(e){await this.loadCatalogsPromise;const s=Array.from(this.messagesById.values()),t=await this.getPersister(),a=e===this.config.extract.sourceLocale,o=this.lastWriteByLocale.get(e),r=await t.getLastModified(e);r&&o&&r>o&&await this.reloadLocaleCatalog(e);const i=a?this.messagesById:this.translationsByTargetLocale.get(e),c=s.map((e=>{const s=i?.get(e.id);return{...s,id:e.id,description:e.description,references:e.references,message:a?e.message:s?.message??""}}));await t.write(c,{locale:e,sourceMessagesById:this.messagesById});const n=await t.getLastModified(e);this.lastWriteByLocale.set(e,n)}onLocalesChange=async e=>{this.loadCatalogsPromise=Promise.all([this.loadCatalogsPromise,...e.added.map((e=>this.reloadLocaleCatalog(e)))]);for(const s of e.added)await this.saveLocale(s);for(const s of e.removed)this.translationsByTargetLocale.delete(s),this.lastWriteByLocale.delete(s)};async handleFileEvents(e){this.loadCatalogsPromise&&await this.loadCatalogsPromise,this.scanCompletePromise&&await this.scanCompletePromise;let s=!1;const t=await this.sourceWatcher.expandDirectoryDeleteEvents(e,Array.from(this.sourceMessagesByFile.keys()));for(const e of t.toSorted(((e,s)=>c(e.path,s.path)))){const t=await this.processFile(e.path);s||=t}s&&await this.save()}[Symbol.dispose](){this.sourceWatcher?.stop(),this.sourceWatcher=void 0,this.saveScheduler[Symbol.dispose](),this.catalogLocales&&this.isDevelopment&&this.catalogLocales.unsubscribeLocalesChange(this.onLocalesChange)}}export{d as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"fs/promises";import t from"path";class r{constructor(e){this.messagesPath=e.messagesPath,this.codec=e.codec,this.extension=e.extension}getFileName(e){return e+this.extension}getFilePath(e){return t.join(this.messagesPath,this.getFileName(e))}async read(t){const
|
|
1
|
+
import e from"fs/promises";import t from"path";import{warnAboutMissingReferences as s}from"../utils.js";class r{constructor(e){this.messagesPath=e.messagesPath,this.codec=e.codec,this.extension=e.extension,this.sourceLocale=e.sourceLocale}getFileName(e){return e+this.extension}getFilePath(e){return t.join(this.messagesPath,this.getFileName(e))}async read(t){const s=this.getFilePath(t);let r;try{r=await e.readFile(s,"utf8")}catch(e){if(e&&"object"==typeof e&&"code"in e&&"ENOENT"===e.code)return[];throw new Error(`Error while reading ${this.getFileName(t)}:\n> ${e}`,{cause:e})}try{const e=this.codec.decode(r,{locale:t,sourceLocale:this.sourceLocale});return function(e){for(const t of e){const e=t.segments;if(!Array.isArray(e))continue;const s=e.findIndex((e=>"number"==typeof e));if(-1===s)continue;const r=e.slice(0,s).join(".");throw new Error(`Message at \`${r}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`)}}(e),e}catch(e){throw new Error(`Error while decoding ${this.getFileName(t)}:\n> ${e}`,{cause:e})}}async write(r,o){const i=this.getFilePath(o.locale);s(r);const a=this.codec.encode(r,o);try{const s=t.dirname(i);await e.mkdir(s,{recursive:!0}),await e.writeFile(i,a)}catch(e){console.error(`❌ Failed to write catalog: ${e}`)}}async getLastModified(t){const s=this.getFilePath(t);try{return(await e.stat(s)).mtime}catch{return}}}export{r as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"path";import t from"icu-minify/compile";import{getFormatExtension as s,resolveCodec as o}from"./format/index.js";import{setNestedProperty as r}from"./utils.js";let
|
|
1
|
+
import e from"path";import t from"icu-minify/compile";import{getFormatExtension as s,resolveCodec as o}from"./format/index.js";import{setNestedProperty as r}from"./utils.js";let n=null,a=!1;const i=new Map;function c(c){const u=this.getOptions(),f=this.async(),d=s(u.messages.format);(async function(e,t){return n||(n=await o(e.messages.format,t)),n})(u,this.rootContext).then((s=>{!function(e){if(a||!e.toJSONString)return;a=!0,console.warn("`toJSONString` is deprecated and no longer needed, please remove it.")}(s);const o=e.basename(this.resourcePath,d),n=s.decode(c,{locale:o,sourceLocale:u.messages.sourceLocale});let m;if(u.messages.precompile){m=function(e,s){const o=Object.create(null),n=new Set(s.keys());for(const a of e){n.delete(a.id),l(a);const e=a.message,i=s.get(a.id);let c;i?.messageValue===e?c=i.compiledMessage:(c=t(e),s.set(a.id,{compiledMessage:c,messageValue:e})),r(o,a.id,c)}for(const e of n)s.delete(e);return o}(n,function(e){let t=i.get(e);return t||(t=new Map,i.set(e,t)),t}(this.resourcePath))}else m=function(e){const t=Object.create(null);for(const s of e)l(s),r(t,s.id,s.message);return t}(n);const g=`export default JSON.parse(${JSON.stringify(JSON.stringify(m))});`;f(null,g)})).catch(f)}function l(e){const t=e.message;if(Array.isArray(t))throw new Error(`Message at \`${e.id}\` resolved to an array, but only strings are supported. See https://next-intl.dev/docs/usage/translations#arrays-of-messages`);if("object"==typeof t)throw new Error(`Message at \`${e.id}\` resolved to \`${typeof t}\`, but only strings are supported. Use a \`.\` to retrieve nested messages. See https://next-intl.dev/docs/usage/translations#structuring-messages`)}export{c as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire as e}from"module";import t from"path";import{transform as s}from"@swc/core";import{getDefaultProjectRoot as
|
|
1
|
+
import{createRequire as e}from"module";import t from"path";import{transform as s}from"@swc/core";import{getDefaultProjectRoot as r,normalizePathToPosix as o}from"../utils.js";import i from"./LRUCache.js";const c=e(import.meta.url);class a{compileCache=new i(750);constructor(e){this.isDevelopment=e.isDevelopment??!1,this.projectRoot=e.projectRoot??r(),this.sourceMap=e.sourceMap??!1}async extract(e,r){const i=[r,e].join("!"),a=this.compileCache.get(i);if(a)return a;if(!r.includes("useExtracted")&&!r.includes("getExtracted"))return{messages:[],code:r};const p=o(t.relative(this.projectRoot,e)),n=await s(r,{jsc:{target:"esnext",parser:{syntax:"typescript",tsx:!0,decorators:!0},experimental:{cacheRoot:"node_modules/.cache/swc",disableBuiltinTransformsForInternalTesting:!0,disableAllLints:!0,plugins:[[c.resolve("next-intl-swc-plugin-extractor"),{isDevelopment:this.isDevelopment,filePath:p}]]}},sourceMaps:this.sourceMap,sourceFileName:p,filename:p}),l=n.output,m=JSON.parse(JSON.parse(l).results).filter((e=>"extracted"===e.type)).map((e=>({id:e.id,message:e.message,description:e.description,reference:e.reference}))),u={code:n.code,map:n.map,messages:m};return this.compileCache.set(i,u),u}}export{a as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"@eloqnt/format-po";import{defineCodec as t}from"../ExtractorCodec.js";var o=t((()=>{const t=e();return{...t,decode:(e,o)=>(function(e){if(e.includes("X-Crowdin-SourceKey: msgstr"))throw new Error("This catalog uses the previous PO layout of next-intl, which stored message keys in `msgid`. The PO format now follows the gettext convention: the source text in `msgid` and the message key in `msgctxt`. Please migrate your catalogs, or use a custom codec to keep the previous layout: https://github.com/amannn/next-intl/pull/2393")}(e),t.decode(e,o))}}));export{o as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"path";import{throwError as
|
|
1
|
+
import{createRequire as o}from"module";import e from"path";import{pathToFileURL as t}from"url";import{throwError as n}from"../../plugin/utils.js";const c={json:{codec:()=>import("@eloqnt/format-json"),extension:".json"},po:{codec:()=>import("./codecs/BuiltInPoCodec.js"),extension:".po"}};function r(o){return"string"==typeof o&&o in c}function i(o){return r(o)?c[o].extension:o.extension}async function s(i,s){if(r(i)){return(0,(await c[i].codec()).default)()}{let c,r;if(i.codec.startsWith(".")||e.isAbsolute(i.codec))c=e.resolve(s,i.codec);else try{c=o(e.join(s,"noop.js")).resolve(i.codec)}catch(o){n(`Could not resolve the codec package "${i.codec}". Is it installed?\n${o}`)}try{r=await import(t(c).href)}catch(o){n(`Could not load codec from "${c}".\n${o}`)}const d=r.default;return d&&"function"==typeof d||n(`Codec at "${c}" must have a default export returned from \`defineCodec\`.`),d()}}export{c as default,i as getFormatExtension,s as resolveCodec};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import t from"path";import{warn as e}from"../plugin/utils.js";function n(e){return t.posix.normalize(e.split(t.win32.sep).join(t.posix.sep))}const o=new Set(["__proto__","constructor","prototype"]);function r(t){return o.has(t)}function c(t){const{locales:e}=t.extract;return"infer"===e||e.length>0}function i(t,e,n){const o=e.split(".");for(const t of o)if(r(t))throw new Error(`Invalid message id segment: ${t}`);let c=t;for(let t=0;t<o.length-1;t++){const e=o[t];Object.prototype.hasOwnProperty.call(c,e)&&"object"==typeof c[e]&&null!==c[e]||(c[e]=Object.create(null)),c=c[e]}c[o[o.length-1]]=n}function s(t){const n=new Set;for(const o of t)null!=o.references[0]||n.has(o.id)||(n.add(o.id),e(`Missing file reference for extracted message: ${o.id}`))}function l(t,e){return t.localeCompare(e,"en")}function f(t,e){const n=l(t.path,e.path);return 0!==n?n:(t.line??0)-(e.line??0)}function p(){return process.cwd()}export{f as compareReferences,p as getDefaultProjectRoot,c as hasLocalesToExtract,r as isForbiddenObjectKey,l as localeCompare,n as normalizePathToPosix,i as setNestedProperty,s as warnAboutMissingReferences};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"fs";import{createRequire as t}from"module";import r from"path";import{getFormatExtension as
|
|
1
|
+
import e from"fs";import{createRequire as t}from"module";import r from"path";import{getFormatExtension as o}from"../extractor/format/index.js";import{normalizeMessagesCatalogPaths as s}from"../extractor/normalizeExtractorConfig.js";import n from"../extractor/source/SourceFileFilter.js";import{isDevelopmentOrNextBuild as i}from"./config.js";import{isNextJs16OrHigher as a,hasStableTurboConfig as l}from"./nextFlags.js";import{throwError as u}from"./utils.js";const c=t(import.meta.url);function m(e){return[`${e}.ts`,`${e}.tsx`,`${e}.js`,`${e}.jsx`]}function p(t,o){function s(t){return e.existsSync(function(e){const t=[];return o&&t.push(o),t.push(e),r.resolve(...t)}(t))}if(t)return i&&!s(t)&&u(`Could not find i18n config at ${t}, please provide a valid path.`),t;for(const e of[...m("./i18n/request"),...m("./src/i18n/request")])if(s(e))return e;return i&&u("Could not locate request configuration module.\n\nThis path is supported by default: ./(src/)i18n/request.{js,jsx,ts,tsx}\n\nAlternatively, you can specify a custom location in your Next.js config:\n\nconst withNextIntl = createNextIntlPlugin(\n './path/to/i18n/request.tsx'\n);"),s("./src")?"./src/i18n/request.ts":"./i18n/request.ts"}function f(e,t,i){const m=null!=process.env.TURBOPACK,f=m||a(),x={};let g=[];function d(e){return{loader:"next-intl/extractor/extractionLoader",options:e}}function v(){const t=e.experimental,r=t.messages,o=r.sourceLocale??("object"==typeof t.extract?t.extract.sourceLocale:void 0);return{loader:"next-intl/extractor/catalogLoader",options:{messages:{format:r.format,...void 0!==r.precompile&&{precompile:r.precompile},sourceLocale:o}}}}function h(){return t?.turbopack?.rules||t?.experimental?.turbo?.rules||{}}function j(e,t,r){e[t]?Array.isArray(e[t])?e[t].push(r):e[t]=[e[t],r]:e[t]=r}if(e.experimental?.messages&&(g=s(e.experimental.messages.path)),f){e.requestConfig&&r.isAbsolute(e.requestConfig)&&u("Turbopack support for next-intl currently does not support absolute paths, please provide a relative one (e.g. './src/i18n/config.ts').\n\nFound: "+e.requestConfig);const s={"next-intl/config":p(e.requestConfig)};if(e.experimental?.messages?.precompile){let e=r.relative(process.cwd(),c.resolve("use-intl/format-message/format-only"));e.startsWith(".")||(e=`./${e}`),s["use-intl/format-message"]=e.replace(/\\/g,"/")}let m;if(e.experimental?.extract&&(a()||u("Message extraction requires Next.js 16 or higher."),m??=h(),j(m,`*.{${n.EXTENSIONS.join(",")}}`,{loaders:[d(i)],condition:{content:/(useExtracted|getExtracted)/}})),e.experimental?.messages){a()||u("Message catalog loading requires Next.js 16 or higher."),m??=h();j(m,`*${o(e.experimental.messages.format)}`,{loaders:[v()],condition:{path:`{${g.join(",")}}/**/*`},as:"*.js"})}l()&&!t?.experimental?.turbo?x.turbopack={...t?.turbopack,...m&&{rules:m},resolveAlias:{...t?.turbopack?.resolveAlias,...s}}:x.experimental={...t?.experimental,turbo:{...t?.experimental?.turbo,...m&&{rules:m},resolveAlias:{...t?.experimental?.turbo?.resolveAlias,...s}}}}return m||(x.webpack=function(s,a){if(s.resolve||(s.resolve={}),s.resolve.alias||(s.resolve.alias={}),s.resolve.alias["next-intl/config"]=r.resolve(s.context,p(e.requestConfig,s.context)),e.experimental?.messages?.precompile&&(s.resolve.alias["use-intl/format-message"]=c.resolve("use-intl/format-message/format-only")),e.experimental?.extract&&(s.module||(s.module={}),s.module.rules||(s.module.rules=[]),s.module.rules.push({test:new RegExp(`\\.(${n.EXTENSIONS.join("|")})$`),use:[d(i)]})),e.experimental?.messages){s.module||(s.module={}),s.module.rules||(s.module.rules=[]);const t=o(e.experimental.messages.format);s.module.rules.push({test:new RegExp(`${t.replace(/\./g,"\\.")}$`),include:g.map((e=>r.resolve(s.context,e))),use:[v()],type:"javascript/auto"})}return"function"==typeof t?.webpack?t.webpack(s,a):s}),t?.trailingSlash&&(x.env={...t.env,_next_intl_trailing_slash:"true"}),Object.assign({},t,x)}export{f as default};
|
|
@@ -15,7 +15,7 @@ export default class CatalogManager implements Disposable {
|
|
|
15
15
|
private sourceMessagesByFile;
|
|
16
16
|
/**
|
|
17
17
|
* Reverse index for rebuilding aggregated messages without scanning all files.
|
|
18
|
-
* Contains the same `
|
|
18
|
+
* Contains the same `SourceExtractedMessage` arrays as `sourceMessagesByFile` and is
|
|
19
19
|
* kept in sync with it.
|
|
20
20
|
*/
|
|
21
21
|
private sourceMessagesById;
|
|
@@ -4,10 +4,12 @@ export default class CatalogPersister {
|
|
|
4
4
|
private messagesPath;
|
|
5
5
|
private codec;
|
|
6
6
|
private extension;
|
|
7
|
+
private sourceLocale;
|
|
7
8
|
constructor(params: {
|
|
8
9
|
messagesPath: string;
|
|
9
10
|
codec: ExtractorCodec;
|
|
10
11
|
extension: string;
|
|
12
|
+
sourceLocale: Locale;
|
|
11
13
|
});
|
|
12
14
|
private getFileName;
|
|
13
15
|
private getFilePath;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SourceExtractedMessage } from '../types.js';
|
|
2
2
|
export default class MessageExtractor {
|
|
3
3
|
private isDevelopment;
|
|
4
4
|
private projectRoot;
|
|
@@ -10,7 +10,7 @@ export default class MessageExtractor {
|
|
|
10
10
|
sourceMap?: boolean;
|
|
11
11
|
});
|
|
12
12
|
extract(absoluteFilePath: string, source: string): Promise<{
|
|
13
|
-
messages: Array<
|
|
13
|
+
messages: Array<SourceExtractedMessage>;
|
|
14
14
|
code: string;
|
|
15
15
|
map?: string;
|
|
16
16
|
}>;
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { defineCodec as sharedDefineCodec } from '@eloqnt/config';
|
|
2
|
+
import type { Locale } from '../types.js';
|
|
3
|
+
type SeparateFileCodec = Extract<ReturnType<ReturnType<typeof sharedDefineCodec>>, {
|
|
4
|
+
decode: unknown;
|
|
5
|
+
}>;
|
|
6
|
+
export default interface ExtractorCodec extends SeparateFileCodec {
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* @deprecated No longer used. Catalogs are loaded into your application via
|
|
9
|
+
* `decode`, so you can remove `toJSONString` from your codec. Providing it
|
|
10
|
+
* logs a deprecation warning and has no effect.
|
|
9
11
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* Encode a list of extracted messages into a string that can be written as
|
|
13
|
-
* file content to the disk.
|
|
14
|
-
*/
|
|
15
|
-
encode(messages: Array<ExtractorMessage>, context: ExtractorCodecContext & {
|
|
16
|
-
sourceMessagesById: Map</* ID */ string, ExtractorMessage>;
|
|
12
|
+
toJSONString?(content: string, context: {
|
|
13
|
+
locale: Locale;
|
|
17
14
|
}): string;
|
|
18
|
-
/**
|
|
19
|
-
* Turns the content of a file into a JSON string that represents extracted
|
|
20
|
-
* messages. The returned value will be passed to `JSON.parse`.
|
|
21
|
-
*
|
|
22
|
-
* @return E.g. `[{"id":"-YJVTi","message":"Hey!"}]`
|
|
23
|
-
*
|
|
24
|
-
* This is used when loading messages into your application, typically via a
|
|
25
|
-
* dynamic import (e.g. `import(`../messages/${locale}.json`)`).
|
|
26
|
-
*
|
|
27
|
-
* If your file content is JSON and should be used as-is, you can set this to
|
|
28
|
-
* an identity function.
|
|
29
|
-
*/
|
|
30
|
-
toJSONString(content: string, context: ExtractorCodecContext): string;
|
|
31
15
|
}
|
|
32
16
|
export declare function defineCodec(factory: () => ExtractorCodec): () => ExtractorCodec;
|
|
33
17
|
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
declare const _default: () => {
|
|
2
|
+
encode(messages: {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
id: string;
|
|
5
|
+
message: string;
|
|
6
|
+
description: Array<string>;
|
|
7
|
+
references: Array<{
|
|
8
|
+
path: string;
|
|
9
|
+
line?: number;
|
|
10
|
+
}>;
|
|
11
|
+
segments?: Array<string | number>;
|
|
12
|
+
verbatim?: {
|
|
13
|
+
value: unknown;
|
|
14
|
+
};
|
|
15
|
+
orphan?: boolean;
|
|
16
|
+
}[], context: {
|
|
17
|
+
locale: string;
|
|
18
|
+
sourceMessagesById: Map<string, {
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
id: string;
|
|
21
|
+
message: string;
|
|
22
|
+
description: Array<string>;
|
|
23
|
+
references: Array<{
|
|
24
|
+
path: string;
|
|
25
|
+
line?: number;
|
|
26
|
+
}>;
|
|
27
|
+
segments?: Array<string | number>;
|
|
28
|
+
verbatim?: {
|
|
29
|
+
value: unknown;
|
|
30
|
+
};
|
|
31
|
+
orphan?: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
}): string;
|
|
34
|
+
decode(content: string, context: {
|
|
35
|
+
locale: string;
|
|
36
|
+
sourceLocale: string;
|
|
37
|
+
}): {
|
|
38
|
+
formatPoEmptySource?: string | undefined;
|
|
39
|
+
formatPoContext?: string | undefined;
|
|
40
|
+
id: string;
|
|
41
|
+
message: string;
|
|
42
|
+
description: string[];
|
|
43
|
+
references: {
|
|
44
|
+
path: string;
|
|
45
|
+
line?: number;
|
|
46
|
+
}[];
|
|
47
|
+
flags?: Array<string>;
|
|
48
|
+
}[];
|
|
49
|
+
};
|
|
50
|
+
export default _default;
|
|
@@ -2,11 +2,11 @@ import type ExtractorCodec from './ExtractorCodec.js';
|
|
|
2
2
|
import type { MessagesFormat } from './types.js';
|
|
3
3
|
declare const formats: {
|
|
4
4
|
json: {
|
|
5
|
-
codec: () => Promise<typeof import("
|
|
5
|
+
codec: () => Promise<typeof import("@eloqnt/format-json")>;
|
|
6
6
|
extension: ".json";
|
|
7
7
|
};
|
|
8
8
|
po: {
|
|
9
|
-
codec: () => Promise<typeof import("./codecs/
|
|
9
|
+
codec: () => Promise<typeof import("./codecs/BuiltInPoCodec.js")>;
|
|
10
10
|
extension: ".po";
|
|
11
11
|
};
|
|
12
12
|
};
|