@inquirer/i18n 1.3.4 → 1.4.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 CHANGED
@@ -107,7 +107,11 @@ import { createLocalizedPrompts, registerLocale } from '@inquirer/i18n';
107
107
  import type { Locale } from '@inquirer/i18n';
108
108
 
109
109
  const deLocale: Locale = {
110
- confirm: { yesLabel: 'Ja', noLabel: 'Nein' },
110
+ confirm: {
111
+ yesLabel: 'Ja',
112
+ noLabel: 'Nein',
113
+ invalidAnswer: (yes, no) => `Sie müssen mit "${yes}" oder "${no}" antworten.`,
114
+ },
111
115
  select: { helpNavigate: 'Navigieren', helpSelect: 'Auswählen' },
112
116
  checkbox: {
113
117
  helpNavigate: 'Navigieren',
@@ -121,7 +125,10 @@ const deLocale: Locale = {
121
125
  loadingMessage: () => 'Überprüfung...',
122
126
  waitingMessage: (enterKey) => `Drücken Sie ${enterKey}, um Ihren Editor zu öffnen.`,
123
127
  },
124
- password: { maskedText: '[Eingabe verborgen]' },
128
+ password: {
129
+ maskedText: '[Eingabe verborgen]',
130
+ helpToggle: 'Sichtbarkeit umschalten',
131
+ },
125
132
  };
126
133
 
127
134
  // Option A — use directly
@@ -135,6 +142,33 @@ registerLocale('de', createLocalizedPrompts(deLocale));
135
142
  `import { confirm } from '@inquirer/i18n'` will pick the German locale automatically
136
143
  whenever `LANG` (or the other sources) resolve to `de`.
137
144
 
145
+ # Themes and locales
146
+
147
+ The strings above are the localized defaults. A `theme` passed to a prompt applies
148
+ in every language — it overrides the locale strings, exactly as it overrides the
149
+ built-in English ones in `@inquirer/prompts`. For example
150
+ `password({ message, theme: { style: { maskedText: '[***]' } } })` renders `[***]`
151
+ under every locale.
152
+
153
+ To change the words of a single language, don't put them in the theme: build a
154
+ locale with `createLocalizedPrompts` and register it with `registerLocale` instead:
155
+
156
+ ```js
157
+ import { createLocalizedPrompts, registerLocale } from '@inquirer/i18n';
158
+ import { locale as zh } from '@inquirer/i18n/zh';
159
+
160
+ registerLocale(
161
+ 'zh',
162
+ createLocalizedPrompts({
163
+ ...zh,
164
+ password: { ...zh.password, maskedText: '[已隐藏]' },
165
+ }),
166
+ );
167
+ ```
168
+
169
+ Note: a custom `keysHelpTip` formatter receives the help-line labels already
170
+ translated into the active language (e.g. `导航` for `navigate` under `zh`).
171
+
138
172
  # License
139
173
 
140
174
  Copyright (c) 2025 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
package/dist/create.js CHANGED
@@ -1,6 +1,81 @@
1
1
  import { checkbox as checkboxPrompt, confirm as confirmPrompt, editor as editorPrompt, expand as expandPrompt, input as inputPrompt, number as numberPrompt, password as passwordPrompt, rawlist as rawlistPrompt, search as searchPrompt, select as selectPrompt, } from '@inquirer/prompts';
2
2
  import { makeTheme } from '@inquirer/core';
3
3
  import { styleText } from 'node:util';
4
+ /**
5
+ * Default help-line renderer, with the same layout as every prompt's built-in
6
+ * theme: a bold key followed by a dim label, joined with a dim separator.
7
+ */
8
+ function defaultKeysHelpTip(keys) {
9
+ return keys
10
+ .map(([key, label]) => `${styleText('bold', key)} ${styleText('dim', label)}`)
11
+ .join(styleText('dim', ' • '));
12
+ }
13
+ /**
14
+ * The user's keysHelpTip formatter from `config.theme`, or the default
15
+ * renderer when they gave none.
16
+ *
17
+ * Merged through `makeTheme` because `config.theme` is a `PartialDeep`, which
18
+ * widens function members to `{}` and loses their signature — `makeTheme`
19
+ * returns the fully typed theme, restoring it.
20
+ */
21
+ function resolveKeysHelpTip(userTheme) {
22
+ const theme = makeTheme({ style: { keysHelpTip: defaultKeysHelpTip } }, userTheme);
23
+ return theme.style.keysHelpTip;
24
+ }
25
+ /**
26
+ * Wrap a help-line renderer so it receives the labels translated into the
27
+ * active locale instead of the prompt's built-in English ones.
28
+ */
29
+ function localizedKeysHelpTip(keysHelpTip, translate) {
30
+ return (keys) => keysHelpTip(keys.map(([key, label]) => [key, translate(label)]));
31
+ }
32
+ /**
33
+ * The user's theme minus its `keysHelpTip`: the wrapper installs its own
34
+ * composing formatter, which already delegates to the user's one with
35
+ * translated labels. Keeping the original in the merge would override the
36
+ * composer and pass English labels straight through.
37
+ */
38
+ function themeWithoutKeysHelpTip(theme) {
39
+ const style = { ...theme.style };
40
+ delete style.keysHelpTip;
41
+ return { ...theme, style };
42
+ }
43
+ /** Translate the select prompt's built-in help labels. */
44
+ function translateSelectLabel(strings, label) {
45
+ if (label === 'navigate')
46
+ return strings.helpNavigate;
47
+ if (label === 'select')
48
+ return strings.helpSelect;
49
+ return label;
50
+ }
51
+ /** Translate the checkbox prompt's built-in help labels. */
52
+ function translateCheckboxLabel(strings, label) {
53
+ if (label === 'navigate')
54
+ return strings.helpNavigate;
55
+ if (label === 'select')
56
+ return strings.helpSelect;
57
+ if (label === 'submit')
58
+ return strings.helpSubmit;
59
+ if (label === 'all')
60
+ return strings.helpAll;
61
+ if (label === 'invert')
62
+ return strings.helpInvert;
63
+ return label;
64
+ }
65
+ /** Translate the search prompt's built-in help labels. */
66
+ function translateSearchLabel(strings, label) {
67
+ if (label === 'navigate')
68
+ return strings.helpNavigate;
69
+ if (label === 'select')
70
+ return strings.helpSelect;
71
+ return label;
72
+ }
73
+ /** Translate the password prompt's built-in help labels. */
74
+ function translatePasswordLabel(strings, label) {
75
+ if (label === 'toggle visibility')
76
+ return strings.helpToggle;
77
+ return label;
78
+ }
4
79
  /**
5
80
  * Factory function that creates localized prompt wrappers for a given locale.
6
81
  *
@@ -12,77 +87,41 @@ export function createLocalizedPrompts(locale) {
12
87
  confirm(config, context) {
13
88
  // The labels are the keywords: confirm shows their first character in
14
89
  // the hint, matches them prefix-based, and displays the label once done.
15
- const theme = {
16
- ...config.theme,
90
+ // Locale strings are the defaults; config.theme overrides them.
91
+ const theme = makeTheme({
17
92
  keywords: {
18
93
  yes: locale.confirm.yesLabel,
19
94
  no: locale.confirm.noLabel,
20
95
  error: ({ yes, no }) => locale.confirm.invalidAnswer(yes, no),
21
96
  },
22
- };
97
+ }, config.theme);
23
98
  return confirmPrompt({ ...config, theme }, context);
24
99
  },
25
100
  select(config, context) {
26
- const theme = makeTheme(config.theme, {
101
+ const localeTheme = {
27
102
  style: {
28
- keysHelpTip: (keys) => {
29
- const localizedKeys = keys.map(([key, label]) => {
30
- if (label === 'navigate')
31
- return [key, locale.select.helpNavigate];
32
- if (label === 'select')
33
- return [key, locale.select.helpSelect];
34
- return [key, label];
35
- });
36
- return localizedKeys
37
- .map(([k, l]) => `${styleText('bold', k)} ${styleText('dim', l)}`)
38
- .join(styleText('dim', ' • '));
39
- },
103
+ keysHelpTip: localizedKeysHelpTip(resolveKeysHelpTip(config.theme), (label) => translateSelectLabel(locale.select, label)),
40
104
  },
41
- });
105
+ };
106
+ const theme = makeTheme(localeTheme, themeWithoutKeysHelpTip(config.theme ?? {}));
42
107
  return selectPrompt({ ...config, theme }, context);
43
108
  },
44
109
  checkbox(config, context) {
45
- const theme = makeTheme(config.theme, {
110
+ const localeTheme = {
46
111
  style: {
47
- keysHelpTip: (keys) => {
48
- const localizedKeys = keys.map(([key, label]) => {
49
- if (label === 'navigate')
50
- return [key, locale.checkbox.helpNavigate];
51
- if (label === 'select')
52
- return [key, locale.checkbox.helpSelect];
53
- if (label === 'submit')
54
- return [key, locale.checkbox.helpSubmit];
55
- if (label === 'all')
56
- return [key, locale.checkbox.helpAll];
57
- if (label === 'invert')
58
- return [key, locale.checkbox.helpInvert];
59
- return [key, label];
60
- });
61
- return localizedKeys
62
- .map(([k, l]) => `${styleText('bold', k)} ${styleText('dim', l)}`)
63
- .join(styleText('dim', ' • '));
64
- },
112
+ keysHelpTip: localizedKeysHelpTip(resolveKeysHelpTip(config.theme), (label) => translateCheckboxLabel(locale.checkbox, label)),
65
113
  },
66
- });
114
+ };
115
+ const theme = makeTheme(localeTheme, themeWithoutKeysHelpTip(config.theme ?? {}));
67
116
  return checkboxPrompt({ ...config, theme }, context);
68
117
  },
69
118
  search(config, context) {
70
- const theme = makeTheme(config.theme, {
119
+ const localeTheme = {
71
120
  style: {
72
- keysHelpTip: (keys) => {
73
- const localizedKeys = keys.map(([key, label]) => {
74
- if (label === 'navigate')
75
- return [key, locale.search.helpNavigate];
76
- if (label === 'select')
77
- return [key, locale.search.helpSelect];
78
- return [key, label];
79
- });
80
- return localizedKeys
81
- .map(([k, l]) => `${styleText('bold', k)} ${styleText('dim', l)}`)
82
- .join(styleText('dim', ' • '));
83
- },
121
+ keysHelpTip: localizedKeysHelpTip(resolveKeysHelpTip(config.theme), (label) => translateSearchLabel(locale.search, label)),
84
122
  },
85
- });
123
+ };
124
+ const theme = makeTheme(localeTheme, themeWithoutKeysHelpTip(config.theme ?? {}));
86
125
  return searchPrompt({ ...config, theme }, context);
87
126
  },
88
127
  expand(config, context) {
@@ -92,12 +131,13 @@ export function createLocalizedPrompts(locale) {
92
131
  return rawlistPrompt(config, context);
93
132
  },
94
133
  editor(config, context) {
95
- const theme = makeTheme(config.theme, {
134
+ const localeTheme = {
96
135
  style: {
97
136
  loadingMessage: locale.editor.loadingMessage,
98
137
  waitingMessage: locale.editor.waitingMessage,
99
138
  },
100
- });
139
+ };
140
+ const theme = makeTheme(localeTheme, config.theme);
101
141
  return editorPrompt({ ...config, theme }, context);
102
142
  },
103
143
  input(config, context) {
@@ -107,21 +147,13 @@ export function createLocalizedPrompts(locale) {
107
147
  return numberPrompt(config, context);
108
148
  },
109
149
  password(config, context) {
110
- const theme = makeTheme(config.theme, {
150
+ const localeTheme = {
111
151
  style: {
112
152
  maskedText: locale.password.maskedText,
113
- keysHelpTip: (keys) => {
114
- const localizedKeys = keys.map(([key, label]) => {
115
- if (label === 'toggle visibility')
116
- return [key, locale.password.helpToggle];
117
- return [key, label];
118
- });
119
- return localizedKeys
120
- .map(([k, l]) => `${styleText('bold', k)} ${styleText('dim', l)}`)
121
- .join(styleText('dim', ' • '));
122
- },
153
+ keysHelpTip: localizedKeysHelpTip(resolveKeysHelpTip(config.theme), (label) => translatePasswordLabel(locale.password, label)),
123
154
  },
124
- });
155
+ };
156
+ const theme = makeTheme(localeTheme, themeWithoutKeysHelpTip(config.theme ?? {}));
125
157
  return passwordPrompt({ ...config, theme }, context);
126
158
  },
127
159
  };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import * as en from "./locales/en.js";
2
- import * as es from "./locales/es.js";
3
- import * as fr from "./locales/fr.js";
4
- import * as pt from "./locales/pt.js";
5
- import * as zh from "./locales/zh.js";
6
- export * from "./create.js";
1
+ import * as en from './locales/en.js';
2
+ import * as es from './locales/es.js';
3
+ import * as fr from './locales/fr.js';
4
+ import * as pt from './locales/pt.js';
5
+ import * as zh from './locales/zh.js';
6
+ export * from './create.js';
7
7
  const localeMap = { en, es, fr, pt, zh };
8
8
  /**
9
9
  * Register a custom locale so it is automatically picked up by the