next-intl 4.5.7 → 4.6.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.
Files changed (78) hide show
  1. package/dist/cjs/development/ExtractorCodec-DZKNn0Zq.cjs +37 -0
  2. package/dist/cjs/development/JSONCodec-Dlcx71xz.cjs +41 -0
  3. package/dist/cjs/development/POCodec-BW-UDNcq.cjs +94 -0
  4. package/dist/cjs/development/plugin.cjs +28 -5
  5. package/dist/esm/development/extractor/ExtractionCompiler.js +22 -25
  6. package/dist/esm/development/extractor/catalog/CatalogLocales.js +0 -33
  7. package/dist/esm/development/extractor/catalog/CatalogManager.js +134 -102
  8. package/dist/esm/development/extractor/catalog/CatalogPersister.js +31 -13
  9. package/dist/esm/development/extractor/catalog/SaveScheduler.js +33 -14
  10. package/dist/esm/development/extractor/catalogLoader.js +10 -10
  11. package/dist/esm/development/extractor/extractMessages.js +9 -2
  12. package/dist/esm/development/extractor/extractionLoader.js +27 -4
  13. package/dist/esm/development/extractor/extractor/MessageExtractor.js +5 -4
  14. package/dist/esm/development/extractor/format/ExtractorCodec.js +5 -0
  15. package/dist/esm/development/extractor/format/codecs/JSONCodec.js +40 -0
  16. package/dist/esm/development/extractor/format/codecs/POCodec.js +93 -0
  17. package/dist/esm/development/extractor/format/index.js +44 -0
  18. package/dist/esm/development/extractor/source/SourceFileScanner.js +2 -1
  19. package/dist/esm/development/extractor/source/SourceFileWatcher.js +38 -0
  20. package/dist/esm/development/extractor/utils.js +16 -1
  21. package/dist/esm/development/extractor.js +1 -0
  22. package/dist/esm/development/plugin/createNextIntlPlugin.js +1 -1
  23. package/dist/esm/development/plugin/getNextConfig.js +7 -4
  24. package/dist/esm/production/extractor/ExtractionCompiler.js +1 -1
  25. package/dist/esm/production/extractor/catalog/CatalogLocales.js +1 -1
  26. package/dist/esm/production/extractor/catalog/CatalogManager.js +1 -1
  27. package/dist/esm/production/extractor/catalog/CatalogPersister.js +1 -1
  28. package/dist/esm/production/extractor/catalog/SaveScheduler.js +1 -1
  29. package/dist/esm/production/extractor/catalogLoader.js +1 -1
  30. package/dist/esm/production/extractor/extractMessages.js +1 -1
  31. package/dist/esm/production/extractor/extractionLoader.js +1 -1
  32. package/dist/esm/production/extractor/extractor/MessageExtractor.js +1 -1
  33. package/dist/esm/production/extractor/format/ExtractorCodec.js +1 -0
  34. package/dist/esm/production/extractor/format/codecs/JSONCodec.js +1 -0
  35. package/dist/esm/production/extractor/format/codecs/POCodec.js +1 -0
  36. package/dist/esm/production/extractor/format/index.js +1 -0
  37. package/dist/esm/production/extractor/source/SourceFileScanner.js +1 -1
  38. package/dist/esm/production/extractor/source/SourceFileWatcher.js +1 -0
  39. package/dist/esm/production/extractor/utils.js +1 -1
  40. package/dist/esm/production/extractor.js +1 -1
  41. package/dist/esm/production/plugin/createNextIntlPlugin.js +1 -1
  42. package/dist/esm/production/plugin/getNextConfig.js +1 -1
  43. package/dist/types/extractor/ExtractionCompiler.d.ts +5 -10
  44. package/dist/types/extractor/catalog/CatalogLocales.d.ts +0 -2
  45. package/dist/types/extractor/catalog/CatalogManager.d.ts +21 -11
  46. package/dist/types/extractor/catalog/CatalogPersister.d.ts +15 -6
  47. package/dist/types/extractor/catalog/SaveScheduler.d.ts +1 -0
  48. package/dist/types/extractor/extractor/MessageExtractor.d.ts +6 -6
  49. package/dist/types/extractor/format/ExtractorCodec.d.ts +33 -0
  50. package/dist/types/extractor/format/codecs/JSONCodec.d.ts +2 -0
  51. package/dist/types/extractor/format/codecs/POCodec.d.ts +2 -0
  52. package/dist/types/extractor/format/codecs/fixtures/JSONCodecStructured.d.ts +2 -0
  53. package/dist/types/extractor/format/codecs/fixtures/POCodecSourceMessageKey.d.ts +2 -0
  54. package/dist/types/extractor/format/index.d.ts +15 -0
  55. package/dist/types/extractor/format/types.d.ts +8 -0
  56. package/dist/types/extractor/index.d.ts +1 -0
  57. package/dist/types/extractor/source/SourceFileFilter.d.ts +1 -1
  58. package/dist/types/extractor/source/SourceFileScanner.d.ts +1 -1
  59. package/dist/types/extractor/source/SourceFileWatcher.d.ts +12 -0
  60. package/dist/types/extractor/types.d.ts +2 -2
  61. package/dist/types/extractor/utils.d.ts +3 -0
  62. package/dist/types/plugin/types.d.ts +1 -1
  63. package/package.json +6 -5
  64. package/dist/esm/development/extractor/formatters/Formatter.js +0 -3
  65. package/dist/esm/development/extractor/formatters/JSONFormatter.js +0 -42
  66. package/dist/esm/development/extractor/formatters/POFormatter.js +0 -51
  67. package/dist/esm/development/extractor/formatters/index.js +0 -6
  68. package/dist/esm/development/extractor/formatters/utils.js +0 -15
  69. package/dist/esm/production/extractor/formatters/Formatter.js +0 -1
  70. package/dist/esm/production/extractor/formatters/JSONFormatter.js +0 -1
  71. package/dist/esm/production/extractor/formatters/POFormatter.js +0 -1
  72. package/dist/esm/production/extractor/formatters/index.js +0 -1
  73. package/dist/esm/production/extractor/formatters/utils.js +0 -1
  74. package/dist/types/extractor/formatters/Formatter.d.ts +0 -10
  75. package/dist/types/extractor/formatters/JSONFormatter.d.ts +0 -10
  76. package/dist/types/extractor/formatters/POFormatter.d.ts +0 -10
  77. package/dist/types/extractor/formatters/index.d.ts +0 -5
  78. package/dist/types/extractor/formatters/utils.d.ts +0 -2
@@ -8,6 +8,7 @@ export default class SaveScheduler<Value> {
8
8
  private isSaving;
9
9
  private delayMs;
10
10
  private pendingResolvers;
11
+ private nextSaveTask?;
11
12
  constructor(delayMs?: number);
12
13
  schedule(saveTask: SaveTask<Value>): Promise<Value>;
13
14
  private scheduleSave;
@@ -1,6 +1,6 @@
1
- import type { ExtractedMessage } from '../types.js';
2
- type StrictExtractedMessage = ExtractedMessage & {
3
- references: NonNullable<ExtractedMessage['references']>;
1
+ import type { ExtractorMessage } from '../types.js';
2
+ type StrictExtractedMessage = ExtractorMessage & {
3
+ references: NonNullable<ExtractorMessage['references']>;
4
4
  };
5
5
  export default class MessageExtractor {
6
6
  private isDevelopment;
@@ -8,11 +8,11 @@ export default class MessageExtractor {
8
8
  private sourceMap;
9
9
  private compileCache;
10
10
  constructor(opts: {
11
- isDevelopment: boolean;
12
- projectRoot: string;
11
+ isDevelopment?: boolean;
12
+ projectRoot?: string;
13
13
  sourceMap?: boolean;
14
14
  });
15
- processFileContent(absoluteFilePath: string, source: string): Promise<{
15
+ extract(absoluteFilePath: string, source: string): Promise<{
16
16
  messages: Array<StrictExtractedMessage>;
17
17
  code: string;
18
18
  map?: string;
@@ -0,0 +1,33 @@
1
+ import type { ExtractorMessage, Locale } from '../types.js';
2
+ type ExtractorCodecContext = {
3
+ locale: Locale;
4
+ };
5
+ export default interface ExtractorCodec {
6
+ /**
7
+ * Decode the content of a file into a list of extracted messages. This is used
8
+ * to load existing messages from disk.
9
+ */
10
+ decode(content: string, context: ExtractorCodecContext): Array<ExtractorMessage>;
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>;
17
+ }): 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
+ }
32
+ export declare function defineCodec(factory: () => ExtractorCodec): () => ExtractorCodec;
33
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => import("../ExtractorCodec.js").default;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => import("../ExtractorCodec.js").default;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => import("../../ExtractorCodec.js").default;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => import("../../ExtractorCodec.js").default;
2
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import type ExtractorCodec from './ExtractorCodec.js';
2
+ import type { MessagesFormat } from './types.js';
3
+ declare const formats: {
4
+ json: {
5
+ codec: () => Promise<typeof import("./codecs/JSONCodec.js")>;
6
+ extension: ".json";
7
+ };
8
+ po: {
9
+ codec: () => Promise<typeof import("./codecs/POCodec.js")>;
10
+ extension: ".po";
11
+ };
12
+ };
13
+ export default formats;
14
+ export declare function getFormatExtension(format: MessagesFormat): string;
15
+ export declare function resolveCodec(format: MessagesFormat, projectRoot: string): Promise<ExtractorCodec>;
@@ -0,0 +1,8 @@
1
+ import type formats from './index.js';
2
+ export type BuiltInMessagesFormat = keyof typeof formats;
3
+ type CustomMessagesFormat = {
4
+ codec: string;
5
+ extension: `.${string}`;
6
+ };
7
+ export type MessagesFormat = BuiltInMessagesFormat | CustomMessagesFormat;
8
+ export {};
@@ -1 +1,2 @@
1
1
  export { default as unstable_extractMessages } from './extractMessages.js';
2
+ export { defineCodec } from './format/ExtractorCodec.js';
@@ -1,6 +1,6 @@
1
1
  export default class SourceFileFilter {
2
2
  static readonly EXTENSIONS: string[];
3
- private static readonly IGNORED_DIRECTORIES;
3
+ static readonly IGNORED_DIRECTORIES: string[];
4
4
  static isSourceFile(filePath: string): boolean;
5
5
  static shouldEnterDirectory(dirPath: string, srcPaths: Array<string>): boolean;
6
6
  private static isIgnoredDirectoryExplicitlyIncluded;
@@ -1,4 +1,4 @@
1
1
  export default class SourceFileScanner {
2
2
  private static walkSourceFiles;
3
- static getSourceFiles(srcPaths: Array<string>): Promise<Array<string>>;
3
+ static getSourceFiles(srcPaths: Array<string>): Promise<Set<string>>;
4
4
  }
@@ -0,0 +1,12 @@
1
+ import { type Event } from '@parcel/watcher';
2
+ type OnChange = (events: Array<Event>) => Promise<void>;
3
+ export default class SourceFileWatcher implements Disposable {
4
+ private subscriptions;
5
+ private roots;
6
+ private onChange;
7
+ constructor(roots: Array<string>, onChange: OnChange);
8
+ start(): Promise<void>;
9
+ stop(): Promise<void>;
10
+ [Symbol.dispose](): void;
11
+ }
12
+ export {};
@@ -1,6 +1,6 @@
1
- export type MessagesFormat = 'json' | 'po';
1
+ import type { MessagesFormat } from './format/types.js';
2
2
  export type Locale = string;
3
- export type ExtractedMessage = {
3
+ export type ExtractorMessage = {
4
4
  id: string;
5
5
  message: string;
6
6
  description?: string;
@@ -1,2 +1,5 @@
1
+ import type { ExtractorMessage } from './types.js';
1
2
  export declare function setNestedProperty(obj: Record<string, any>, keyPath: string, value: any): void;
3
+ export declare function getSortedMessages(messages: Array<ExtractorMessage>): Array<ExtractorMessage>;
2
4
  export declare function localeCompare(a: string, b: string): number;
5
+ export declare function getDefaultProjectRoot(): string;
@@ -1,5 +1,5 @@
1
1
  import type { LoaderContext } from 'webpack';
2
- import type { MessagesFormat } from '../extractor/types.js';
2
+ import type { MessagesFormat } from '../extractor/format/types.js';
3
3
  export type PluginConfig = {
4
4
  requestConfig?: string;
5
5
  experimental?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-intl",
3
- "version": "4.5.7",
3
+ "version": "4.6.0",
4
4
  "sideEffects": false,
5
5
  "author": "Jan Amann <jan@amann.work>",
6
6
  "funding": [
@@ -125,11 +125,12 @@
125
125
  ],
126
126
  "dependencies": {
127
127
  "@formatjs/intl-localematcher": "^0.5.4",
128
+ "@parcel/watcher": "^2.4.1",
128
129
  "@swc/core": "^1.15.2",
129
130
  "negotiator": "^1.0.0",
130
- "next-intl-swc-plugin-extractor": "^4.5.7",
131
- "po-parser": "^1.0.2",
132
- "use-intl": "^4.5.7"
131
+ "next-intl-swc-plugin-extractor": "^4.6.0",
132
+ "po-parser": "^2.0.0",
133
+ "use-intl": "^4.6.0"
133
134
  },
134
135
  "peerDependencies": {
135
136
  "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
@@ -141,5 +142,5 @@
141
142
  "optional": true
142
143
  }
143
144
  },
144
- "gitHead": "2ccc147b68227b32ea451935718c3eb5e73d6e2f"
145
+ "gitHead": "70a047daa5cb235fdcadcf6ebf1daed32ad30c85"
145
146
  }
@@ -1,3 +0,0 @@
1
- class Formatter {}
2
-
3
- export { Formatter as default };
@@ -1,42 +0,0 @@
1
- import { setNestedProperty } from '../utils.js';
2
- import Formatter from './Formatter.js';
3
- import { getSortedMessages } from './utils.js';
4
-
5
- class JSONFormatter extends Formatter {
6
- static NAMESPACE_SEPARATOR = '.';
7
- EXTENSION = '.json';
8
- parse(source) {
9
- const json = JSON.parse(source);
10
- const messages = [];
11
- this.traverseMessages(json, (message, id) => {
12
- messages.push({
13
- id,
14
- message
15
- });
16
- });
17
- return messages;
18
- }
19
- serialize(messages) {
20
- const root = {};
21
- for (const message of getSortedMessages(messages)) {
22
- setNestedProperty(root, message.id, message.message);
23
- }
24
- return JSON.stringify(root, null, 2);
25
- }
26
- toJSONString(source) {
27
- return source;
28
- }
29
- traverseMessages(obj, callback, path = '') {
30
- for (const key of Object.keys(obj)) {
31
- const newPath = path ? path + JSONFormatter.NAMESPACE_SEPARATOR + key : key;
32
- const value = obj[key];
33
- if (typeof value === 'string') {
34
- callback(value, newPath);
35
- } else if (typeof value === 'object') {
36
- this.traverseMessages(value, callback, newPath);
37
- }
38
- }
39
- }
40
- }
41
-
42
- export { JSONFormatter as default };
@@ -1,51 +0,0 @@
1
- import POParser from 'po-parser';
2
- import { setNestedProperty } from '../utils.js';
3
- import Formatter from './Formatter.js';
4
- import { getSortedMessages } from './utils.js';
5
-
6
- class POFormatter extends Formatter {
7
- // See also https://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html
8
- static DEFAULT_METADATA = {
9
- // Recommended by spec
10
- 'Content-Type': 'text/plain; charset=utf-8',
11
- 'Content-Transfer-Encoding': '8bit',
12
- // Otherwise other tools might set this
13
- 'X-Generator': 'next-intl',
14
- // Crowdin defaults to using msgid as source key
15
- 'X-Crowdin-SourceKey': 'msgstr'
16
- };
17
- EXTENSION = '.po';
18
-
19
- // Metadata is stored so it can be retained when writing
20
- metadataByLocale = (() => new Map())();
21
- parse(content, context) {
22
- const catalog = POParser.parse(content);
23
-
24
- // Store metadata for this locale
25
- if (catalog.meta) {
26
- this.metadataByLocale.set(context.locale, catalog.meta);
27
- }
28
- return catalog.messages || [];
29
- }
30
- serialize(messages, context) {
31
- const meta = {
32
- Language: context.locale,
33
- ...POFormatter.DEFAULT_METADATA,
34
- ...this.metadataByLocale.get(context.locale)
35
- };
36
- return POParser.serialize({
37
- meta,
38
- messages: getSortedMessages(messages)
39
- });
40
- }
41
- toJSONString(source, context) {
42
- const parsed = this.parse(source, context);
43
- const messagesObject = {};
44
- for (const message of parsed) {
45
- setNestedProperty(messagesObject, message.id, message.message);
46
- }
47
- return JSON.stringify(messagesObject, null, 2);
48
- }
49
- }
50
-
51
- export { POFormatter as default };
@@ -1,6 +0,0 @@
1
- const formatters = {
2
- json: () => import('./JSONFormatter.js'),
3
- po: () => import('./POFormatter.js')
4
- };
5
-
6
- export { formatters as default };
@@ -1,15 +0,0 @@
1
- import { localeCompare } from '../utils.js';
2
-
3
- function getSortedMessages(messages) {
4
- return messages.toSorted((messageA, messageB) => {
5
- const pathA = messageA.references?.[0]?.path ?? '';
6
- const pathB = messageB.references?.[0]?.path ?? '';
7
- if (pathA === pathB) {
8
- return localeCompare(messageA.id, messageB.id);
9
- } else {
10
- return localeCompare(pathA, pathB);
11
- }
12
- });
13
- }
14
-
15
- export { getSortedMessages };
@@ -1 +0,0 @@
1
- class a{}export{a as default};
@@ -1 +0,0 @@
1
- import{setNestedProperty as s}from"../utils.js";import e from"./Formatter.js";import{getSortedMessages as t}from"./utils.js";class r extends e{static NAMESPACE_SEPARATOR=".";EXTENSION=".json";parse(s){const e=JSON.parse(s),t=[];return this.traverseMessages(e,((s,e)=>{t.push({id:e,message:s})})),t}serialize(e){const r={};for(const o of t(e))s(r,o.id,o.message);return JSON.stringify(r,null,2)}toJSONString(s){return s}traverseMessages(s,e,t=""){for(const o of Object.keys(s)){const a=t?t+r.NAMESPACE_SEPARATOR+o:o,i=s[o];"string"==typeof i?e(i,a):"object"==typeof i&&this.traverseMessages(i,e,a)}}}export{r as default};
@@ -1 +0,0 @@
1
- import t from"po-parser";import{setNestedProperty as e}from"../utils.js";import a from"./Formatter.js";import{getSortedMessages as s}from"./utils.js";class r extends a{static DEFAULT_METADATA={"Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"8bit","X-Generator":"next-intl","X-Crowdin-SourceKey":"msgstr"};EXTENSION=".po";metadataByLocale=(()=>new Map)();parse(e,a){const s=t.parse(e);return s.meta&&this.metadataByLocale.set(a.locale,s.meta),s.messages||[]}serialize(e,a){const o={Language:a.locale,...r.DEFAULT_METADATA,...this.metadataByLocale.get(a.locale)};return t.serialize({meta:o,messages:s(e)})}toJSONString(t,a){const s=this.parse(t,a),r={};for(const t of s)e(r,t.id,t.message);return JSON.stringify(r,null,2)}}export{r as default};
@@ -1 +0,0 @@
1
- const t={json:()=>import("./JSONFormatter.js"),po:()=>import("./POFormatter.js")};export{t as default};
@@ -1 +0,0 @@
1
- import{localeCompare as e}from"../utils.js";function r(r){return r.toSorted(((r,t)=>{const n=r.references?.[0]?.path??"",o=t.references?.[0]?.path??"";return n===o?e(r.id,t.id):e(n,o)}))}export{r as getSortedMessages};
@@ -1,10 +0,0 @@
1
- import type { ExtractedMessage, Locale } from '../types.js';
2
- export type FormatterContext = {
3
- locale: Locale;
4
- };
5
- export default abstract class Formatter {
6
- abstract readonly EXTENSION: `.${string}`;
7
- abstract parse(content: string, context: FormatterContext): Array<ExtractedMessage>;
8
- abstract serialize(messages: Array<ExtractedMessage>, context: FormatterContext): string;
9
- abstract toJSONString(source: string, context: FormatterContext): string;
10
- }
@@ -1,10 +0,0 @@
1
- import type { ExtractedMessage } from '../types.js';
2
- import Formatter from './Formatter.js';
3
- export default class JSONFormatter extends Formatter {
4
- static readonly NAMESPACE_SEPARATOR = ".";
5
- readonly EXTENSION = ".json";
6
- parse(source: string): Array<ExtractedMessage>;
7
- serialize(messages: Array<ExtractedMessage>): string;
8
- toJSONString(source: string): string;
9
- private traverseMessages;
10
- }
@@ -1,10 +0,0 @@
1
- import type { ExtractedMessage } from '../types.js';
2
- import Formatter, { type FormatterContext } from './Formatter.js';
3
- export default class POFormatter extends Formatter {
4
- private static readonly DEFAULT_METADATA;
5
- readonly EXTENSION = ".po";
6
- private metadataByLocale;
7
- parse(content: string, context: FormatterContext): Array<ExtractedMessage>;
8
- serialize(messages: Array<ExtractedMessage>, context: FormatterContext): string;
9
- toJSONString(source: string, context: FormatterContext): string;
10
- }
@@ -1,5 +0,0 @@
1
- declare const formatters: {
2
- json: () => Promise<typeof import("./JSONFormatter.js")>;
3
- po: () => Promise<typeof import("./POFormatter.js")>;
4
- };
5
- export default formatters;
@@ -1,2 +0,0 @@
1
- import type { ExtractedMessage } from '../types.js';
2
- export declare function getSortedMessages(messages: Array<ExtractedMessage>): Array<ExtractedMessage>;