@genrwork/laravel-i18next 0.1.0 → 0.1.1
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 +110 -28
- package/dist/index.cjs +125 -3
- package/dist/index.mjs +124 -1
- package/dist/react.cjs +7 -5
- package/dist/react.mjs +7 -5
- package/dist/shared/{create-i18n-WsDK4Z8L.cjs → create-i18n-DI7Eog6O.cjs} +83 -333
- package/dist/shared/{create-i18n-BSEwKsCX.mjs → create-i18n-DaZzs1DJ.mjs} +78 -333
- package/dist/shared/demand-6YnTqSM4.mjs +92 -0
- package/dist/shared/demand-sbjTXVnl.cjs +101 -0
- package/dist/svelte.cjs +7 -1
- package/dist/svelte.mjs +7 -1
- package/dist/types/contrib/get-plural-index.d.ts +1 -1
- package/dist/types/defaults.d.ts +18 -0
- package/dist/types/demand.d.ts +51 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/plugin/convert.d.ts +10 -0
- package/dist/types/plugin/namespaces.d.ts +20 -0
- package/dist/types/plugin/parser.d.ts +3 -0
- package/dist/types/plugin/sources.d.ts +5 -5
- package/dist/types/preload.d.ts +99 -0
- package/dist/types/react/i18n-provider-props.d.ts +5 -0
- package/dist/types/react/provider.d.ts +4 -3
- package/dist/types/shared/create-i18n.d.ts +9 -3
- package/dist/types/utils/cache.d.ts +3 -0
- package/dist/types/vite.d.ts +18 -1
- package/dist/vite.cjs +408 -6
- package/dist/vite.mjs +408 -6
- package/dist/vue.cjs +2 -1
- package/dist/vue.mjs +2 -1
- package/package.json +155 -155
- package/dist/types/format.d.ts +0 -43
- package/dist/types/interfaces/replacements.d.ts +0 -6
- package/dist/types/utils/replacer.d.ts +0 -9
- /package/dist/types/{utils → plugin}/pluralization.d.ts +0 -0
package/dist/vite.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { normalizePath, createLogger } from 'vite';
|
|
4
|
+
import { D as DECLARED_NAMESPACES_KEY } from './shared/demand-6YnTqSM4.mjs';
|
|
4
5
|
import { fromString } from 'php-array-reader';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -42,10 +43,379 @@ var locale = {
|
|
|
42
43
|
}
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Finds the i18next namespaces a module translates with, by reading its code.
|
|
48
|
+
* Deliberately a text scan and not a parse: it runs on every module of the
|
|
49
|
+
* app, on the code of any framework (JSX, Vue and Svelte output alike), and a
|
|
50
|
+
* namespace it over-reports is harmless -- `preloadI18n()` ignores names that
|
|
51
|
+
* have no language file.
|
|
52
|
+
*
|
|
53
|
+
* Recognized, with string literals only:
|
|
54
|
+
*
|
|
55
|
+
* - `useTranslation('auth')`, `useTranslation(['auth', 'teams'])`
|
|
56
|
+
* (react-i18next, i18next-vue, and this package's Svelte `useTranslation`);
|
|
57
|
+
* - `withTranslation('auth')`;
|
|
58
|
+
* - `ns="auth"` / `ns={'auth'}` (`<Trans>`, `<Translation>`);
|
|
59
|
+
* - `{ ns: 'auth' }` (`t()` options, `getFixedT`).
|
|
60
|
+
*
|
|
61
|
+
* Comments are ignored, so documentation that quotes a call (this package's
|
|
62
|
+
* own, or commented-out code) declares nothing. A namespace requested through
|
|
63
|
+
* a variable is invisible to it: declare it with `declareNamespaces()`.
|
|
64
|
+
*/ const CALL = /\b(?:useTranslation|withTranslation)\s*\(\s*(\[[^\]]*\]|"[^"]*"|'[^']*'|`[^`$]*`)/g;
|
|
65
|
+
const JSX_PROP = /\bns\s*=\s*(?:"([^"]+)"|'([^']+)'|\{\s*(?:"([^"]+)"|'([^']+)')\s*\})/g;
|
|
66
|
+
const OPTION = /\bns\s*:\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
67
|
+
/**
|
|
68
|
+
* A string literal, or a comment: strings are kept, comments dropped. The
|
|
69
|
+
* `//` inside a string (a URL) must not start a comment, which is why both
|
|
70
|
+
* are matched by one pattern, strings first.
|
|
71
|
+
*/ const STRING_OR_COMMENT = /("(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'|`(?:\\.|[^`\\])*`)|\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
|
|
72
|
+
const QUOTED = /["'`]([^"'`]+)["'`]/g;
|
|
73
|
+
function detectNamespaces(source) {
|
|
74
|
+
const found = new Set();
|
|
75
|
+
if (!/useTranslation|withTranslation|\bns\b/.test(source)) return [];
|
|
76
|
+
const code = source.replace(STRING_OR_COMMENT, (_match, string)=>string !== null && string !== void 0 ? string : ' ');
|
|
77
|
+
for (const [, argument] of code.matchAll(CALL)){
|
|
78
|
+
for (const [, name] of argument.matchAll(QUOTED))found.add(name);
|
|
79
|
+
}
|
|
80
|
+
for (const pattern of [
|
|
81
|
+
JSX_PROP,
|
|
82
|
+
OPTION
|
|
83
|
+
]){
|
|
84
|
+
for (const match of code.matchAll(pattern)){
|
|
85
|
+
const name = match.slice(1).find(Boolean);
|
|
86
|
+
if (name) found.add(name);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return Array.from(found).sort();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* eslint-disable */ /**
|
|
93
|
+
* Get the index to use for pluralization.
|
|
94
|
+
* The plural rules are derived from code of the Zend Framework.
|
|
95
|
+
*
|
|
96
|
+
* @category Zend
|
|
97
|
+
* @package Zend_Locale
|
|
98
|
+
* @public https://github.com/zendframework/zf1/blob/master/library/Zend/Translate/Plural.php
|
|
99
|
+
* @copyright 2005-2015 Zend Technologies USA Inc. http://www.zend.com
|
|
100
|
+
* @license http://framework.zend.com/license New BSD License
|
|
101
|
+
*
|
|
102
|
+
* @param {String} locale
|
|
103
|
+
* @param {Number} number
|
|
104
|
+
* @return {Number}
|
|
105
|
+
*/ function getPluralIndex(locale, number) {
|
|
106
|
+
locale = locale.replace('-', '_');
|
|
107
|
+
if (locale === 'pt_BR') {
|
|
108
|
+
// temporary set a locale for brazilian
|
|
109
|
+
locale = 'xbr';
|
|
110
|
+
}
|
|
111
|
+
if (locale.length > 3) {
|
|
112
|
+
locale = locale.substring(0, locale.lastIndexOf('_'));
|
|
113
|
+
}
|
|
114
|
+
switch(locale){
|
|
115
|
+
case 'az':
|
|
116
|
+
case 'bo':
|
|
117
|
+
case 'dz':
|
|
118
|
+
case 'id':
|
|
119
|
+
case 'ja':
|
|
120
|
+
case 'jv':
|
|
121
|
+
case 'ka':
|
|
122
|
+
case 'km':
|
|
123
|
+
case 'kn':
|
|
124
|
+
case 'ko':
|
|
125
|
+
case 'ms':
|
|
126
|
+
case 'th':
|
|
127
|
+
case 'tr':
|
|
128
|
+
case 'vi':
|
|
129
|
+
case 'zh':
|
|
130
|
+
return 0;
|
|
131
|
+
case 'af':
|
|
132
|
+
case 'bn':
|
|
133
|
+
case 'bg':
|
|
134
|
+
case 'ca':
|
|
135
|
+
case 'da':
|
|
136
|
+
case 'de':
|
|
137
|
+
case 'el':
|
|
138
|
+
case 'en':
|
|
139
|
+
case 'eo':
|
|
140
|
+
case 'es':
|
|
141
|
+
case 'et':
|
|
142
|
+
case 'eu':
|
|
143
|
+
case 'fa':
|
|
144
|
+
case 'fi':
|
|
145
|
+
case 'fo':
|
|
146
|
+
case 'fur':
|
|
147
|
+
case 'fy':
|
|
148
|
+
case 'gl':
|
|
149
|
+
case 'gu':
|
|
150
|
+
case 'ha':
|
|
151
|
+
case 'he':
|
|
152
|
+
case 'hu':
|
|
153
|
+
case 'is':
|
|
154
|
+
case 'it':
|
|
155
|
+
case 'ku':
|
|
156
|
+
case 'lb':
|
|
157
|
+
case 'ml':
|
|
158
|
+
case 'mn':
|
|
159
|
+
case 'mr':
|
|
160
|
+
case 'nah':
|
|
161
|
+
case 'nb':
|
|
162
|
+
case 'ne':
|
|
163
|
+
case 'nl':
|
|
164
|
+
case 'nn':
|
|
165
|
+
case 'no':
|
|
166
|
+
case 'om':
|
|
167
|
+
case 'or':
|
|
168
|
+
case 'pa':
|
|
169
|
+
case 'pap':
|
|
170
|
+
case 'ps':
|
|
171
|
+
case 'pt':
|
|
172
|
+
case 'so':
|
|
173
|
+
case 'sq':
|
|
174
|
+
case 'sv':
|
|
175
|
+
case 'sw':
|
|
176
|
+
case 'ta':
|
|
177
|
+
case 'te':
|
|
178
|
+
case 'tk':
|
|
179
|
+
case 'ur':
|
|
180
|
+
case 'zu':
|
|
181
|
+
return number === 1 ? 0 : 1;
|
|
182
|
+
case 'am':
|
|
183
|
+
case 'bh':
|
|
184
|
+
case 'fil':
|
|
185
|
+
case 'fr':
|
|
186
|
+
case 'gun':
|
|
187
|
+
case 'hi':
|
|
188
|
+
case 'ln':
|
|
189
|
+
case 'mg':
|
|
190
|
+
case 'nso':
|
|
191
|
+
case 'xbr':
|
|
192
|
+
case 'ti':
|
|
193
|
+
case 'wa':
|
|
194
|
+
return number === 0 || number === 1 ? 0 : 1;
|
|
195
|
+
case 'be':
|
|
196
|
+
case 'bs':
|
|
197
|
+
case 'hr':
|
|
198
|
+
case 'ru':
|
|
199
|
+
case 'sr':
|
|
200
|
+
case 'uk':
|
|
201
|
+
return number % 10 === 1 && number % 100 !== 11 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2;
|
|
202
|
+
case 'cs':
|
|
203
|
+
case 'sk':
|
|
204
|
+
return number === 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2;
|
|
205
|
+
case 'ga':
|
|
206
|
+
return number === 1 ? 0 : number === 2 ? 1 : 2;
|
|
207
|
+
case 'lt':
|
|
208
|
+
return number % 10 === 1 && number % 100 !== 11 ? 0 : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2;
|
|
209
|
+
case 'sl':
|
|
210
|
+
return number % 100 === 1 ? 0 : number % 100 === 2 ? 1 : number % 100 === 3 || number % 100 === 4 ? 2 : 3;
|
|
211
|
+
case 'mk':
|
|
212
|
+
return number % 10 === 1 ? 0 : 1;
|
|
213
|
+
case 'mt':
|
|
214
|
+
return number === 1 ? 0 : number === 0 || number % 100 > 1 && number % 100 < 11 ? 1 : number % 100 > 10 && number % 100 < 20 ? 2 : 3;
|
|
215
|
+
case 'lv':
|
|
216
|
+
return number === 0 ? 0 : number % 10 === 1 && number % 100 !== 11 ? 1 : 2;
|
|
217
|
+
case 'pl':
|
|
218
|
+
return number === 1 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) ? 1 : 2;
|
|
219
|
+
case 'cy':
|
|
220
|
+
return number === 1 ? 0 : number === 2 ? 1 : number === 8 || number === 11 ? 2 : 3;
|
|
221
|
+
case 'ro':
|
|
222
|
+
return number === 1 ? 0 : number === 0 || number % 100 > 0 && number % 100 < 20 ? 1 : 2;
|
|
223
|
+
case 'ar':
|
|
224
|
+
return number === 0 ? 0 : number === 1 ? 1 : number === 2 ? 2 : number >= 3 && number <= 10 ? 3 : number >= 11 && number <= 99 ? 4 : 5;
|
|
225
|
+
default:
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Select a proper translation string based on the given number,
|
|
232
|
+
* using the same rules as Laravel's `trans_choice()`.
|
|
233
|
+
*
|
|
234
|
+
* @param message
|
|
235
|
+
* @param number
|
|
236
|
+
* @param locale
|
|
237
|
+
*/ function pluralization(message, number, locale) {
|
|
238
|
+
let segments = message.split('|');
|
|
239
|
+
const extracted = extract(segments, number);
|
|
240
|
+
if (extracted !== null) {
|
|
241
|
+
return extracted.trim();
|
|
242
|
+
}
|
|
243
|
+
segments = stripConditions(segments);
|
|
244
|
+
const pluralIndex = getPluralIndex(locale, number);
|
|
245
|
+
return segments.length === 1 || !segments[pluralIndex] ? segments[0] : segments[pluralIndex];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Extract a translation string using inline conditions.
|
|
249
|
+
*
|
|
250
|
+
* @param segments
|
|
251
|
+
* @param number
|
|
252
|
+
*/ function extract(segments, number) {
|
|
253
|
+
for (const segment of segments){
|
|
254
|
+
const result = extractFromString(segment, number);
|
|
255
|
+
if (result !== null) return result;
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get the translation string if the condition matches.
|
|
261
|
+
*
|
|
262
|
+
* @param part
|
|
263
|
+
* @param number
|
|
264
|
+
*/ function extractFromString(part, number) {
|
|
265
|
+
const matches = part.match(/^[{[]([^,{}[\]]*),?([^{}[\]]*)[}\]]([\s\S]*)/);
|
|
266
|
+
if (!matches) return null;
|
|
267
|
+
const [, from, to, value] = matches;
|
|
268
|
+
if ((from === '*' || number >= parseFloat(from)) && (to === '*' || number <= parseFloat(to))) {
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
return from && parseFloat(from) === number ? value : null;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Strip the inline conditions from each segment, just leaving the text.
|
|
275
|
+
*
|
|
276
|
+
* @param segments
|
|
277
|
+
*/ function stripConditions(segments) {
|
|
278
|
+
return segments.map((part)=>part.replace(/^[{[]([^[\]{}]*)[}\]]/, ''));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Converts one Laravel translation message into the i18next way, at build
|
|
283
|
+
* time, so the browser only ever sees i18next JSON:
|
|
284
|
+
*
|
|
285
|
+
* - `:name` becomes `{{name}}`, `:NAME` becomes `{{name, uppercase}}` and
|
|
286
|
+
* `:Name` becomes `{{name, capitalize}}` (formats registered by
|
|
287
|
+
* `registerCaseFormats()`);
|
|
288
|
+
* - a `|` separated message (Laravel's `trans_choice()` forms, with or
|
|
289
|
+
* without `{0}` / `[1,19]` conditions) becomes i18next's plural keys
|
|
290
|
+
* (`key_one`, `key_other`, ...) for the language of the file, plus the raw
|
|
291
|
+
* message under the plain key, which is what Laravel's `trans()` returns
|
|
292
|
+
* when no count is given.
|
|
293
|
+
*/ /**
|
|
294
|
+
* A Laravel placeholder: a colon followed by a name, not glued to a word before
|
|
295
|
+
* it, so `https://`, `10:30` and `Note:this` are text.
|
|
296
|
+
*/ const PLACEHOLDER = /(?<!\w):([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
297
|
+
/**
|
|
298
|
+
* i18next's plural categories, in its own order (see `getSuffixes()`).
|
|
299
|
+
*/ const CATEGORY_ORDER = [
|
|
300
|
+
'zero',
|
|
301
|
+
'one',
|
|
302
|
+
'two',
|
|
303
|
+
'few',
|
|
304
|
+
'many',
|
|
305
|
+
'other'
|
|
306
|
+
];
|
|
307
|
+
/**
|
|
308
|
+
* Whole counts a message is checked against: every count that matters for
|
|
309
|
+
* plural rules, plus a few large ones for the categories that only start there
|
|
310
|
+
* (`many` of Spanish, French and Italian begins at a million).
|
|
311
|
+
*/ const SAMPLES = [
|
|
312
|
+
...Array.from({
|
|
313
|
+
length: 1001
|
|
314
|
+
}, (_, count)=>count),
|
|
315
|
+
1e6,
|
|
316
|
+
2e6,
|
|
317
|
+
1e9
|
|
318
|
+
];
|
|
319
|
+
/**
|
|
320
|
+
* Rewrite Laravel placeholders as i18next interpolations.
|
|
321
|
+
*/ function toInterpolation(message) {
|
|
322
|
+
return message.replace(PLACEHOLDER, (_match, name)=>{
|
|
323
|
+
const hasLetters = name !== name.toLowerCase() || name !== name.toUpperCase();
|
|
324
|
+
if (hasLetters && name === name.toUpperCase()) return `{{${name.toLowerCase()}, uppercase}}`;
|
|
325
|
+
if (name[0] !== name[0].toLowerCase()) return `{{${name[0].toLowerCase()}${name.slice(1)}, capitalize}}`;
|
|
326
|
+
return `{{${name}}}`;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The plural categories a language distinguishes, in i18next's order.
|
|
331
|
+
*/ function categoriesOf(rules) {
|
|
332
|
+
return rules.resolvedOptions().pluralCategories.slice().sort((left, right)=>CATEGORY_ORDER.indexOf(left) - CATEGORY_ORDER.indexOf(right));
|
|
333
|
+
}
|
|
334
|
+
function pluralRulesOf(locale) {
|
|
335
|
+
try {
|
|
336
|
+
return new Intl.PluralRules(locale.replace(/_/g, '-'));
|
|
337
|
+
} catch {
|
|
338
|
+
return new Intl.PluralRules('en');
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Split a pluralized Laravel message into one message per i18next plural
|
|
343
|
+
* category by asking Laravel's own rules (`pluralization()`) what they answer
|
|
344
|
+
* for whole counts, then checking that every count of a category agrees.
|
|
345
|
+
* A message Laravel answers differently inside one category -- `[2,19]` next
|
|
346
|
+
* to `[20,*]` in English -- cannot be written as i18next plural keys and is
|
|
347
|
+
* refused, rather than silently translated wrongly.
|
|
348
|
+
*
|
|
349
|
+
* Count `0` may differ from its category (`{0} none|...`): it is written as
|
|
350
|
+
* `_zero`, which i18next tries first for a count of 0 in every language.
|
|
351
|
+
*/ function pluralForms(key, message, locale) {
|
|
352
|
+
var _a;
|
|
353
|
+
const rules = pluralRulesOf(locale);
|
|
354
|
+
const categories = categoriesOf(rules);
|
|
355
|
+
const forms = new Map();
|
|
356
|
+
SAMPLES.forEach((count)=>{
|
|
357
|
+
if (count === 0) return;
|
|
358
|
+
const category = rules.select(count);
|
|
359
|
+
const text = pluralization(message, count, locale);
|
|
360
|
+
const known = forms.get(category);
|
|
361
|
+
if (known && known.text !== text) {
|
|
362
|
+
throw new Error(`Cannot convert "${key}" (${locale}) to i18next plural forms: Laravel answers "${known.text}" for a count of ` + `${known.count} and "${text}" for ${count}, which i18next treats as the same plural category ("${category}"). ` + 'Split it into separate keys, or write it as i18next JSON.');
|
|
363
|
+
}
|
|
364
|
+
if (!known) forms.set(category, {
|
|
365
|
+
text,
|
|
366
|
+
count
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
const zero = pluralization(message, 0, locale);
|
|
370
|
+
const zeroCategory = rules.select(0);
|
|
371
|
+
const result = {};
|
|
372
|
+
// A category no whole count reaches (`other` in Russian) reads like the largest category.
|
|
373
|
+
const fallback = (_a = [
|
|
374
|
+
...categories
|
|
375
|
+
].reverse().map((category)=>{
|
|
376
|
+
var _a;
|
|
377
|
+
return (_a = forms.get(category)) === null || _a === void 0 ? void 0 : _a.text;
|
|
378
|
+
}).find(Boolean)) !== null && _a !== void 0 ? _a : zero;
|
|
379
|
+
categories.forEach((category)=>{
|
|
380
|
+
var _a, _b;
|
|
381
|
+
result[category] = (_b = (_a = forms.get(category)) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : category === zeroCategory ? zero : fallback;
|
|
382
|
+
});
|
|
383
|
+
if (zero !== result[zeroCategory]) result.zero = zero;
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Convert one entry, returning the i18next entries it becomes (one for a
|
|
388
|
+
* plain message, several for a pluralized one). A list has its messages
|
|
389
|
+
* converted, anything else that is not a string is returned untouched.
|
|
390
|
+
*/ function convertEntry(key, value, locale) {
|
|
391
|
+
if (Array.isArray(value)) {
|
|
392
|
+
return {
|
|
393
|
+
[key]: value.map((item)=>typeof item === 'string' ? toInterpolation(item) : item)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (typeof value !== 'string') return {
|
|
397
|
+
[key]: value
|
|
398
|
+
};
|
|
399
|
+
if (!value.includes('|')) return {
|
|
400
|
+
[key]: toInterpolation(value)
|
|
401
|
+
};
|
|
402
|
+
const entries = {
|
|
403
|
+
[key]: toInterpolation(value)
|
|
404
|
+
};
|
|
405
|
+
const forms = pluralForms(key, value, locale);
|
|
406
|
+
CATEGORY_ORDER.filter((category)=>category in forms).forEach((category)=>{
|
|
407
|
+
entries[`${key}_${category}`] = toInterpolation(forms[category]);
|
|
408
|
+
});
|
|
409
|
+
return entries;
|
|
410
|
+
}
|
|
411
|
+
|
|
45
412
|
/**
|
|
46
413
|
* Convert every `lang/{locale}/{namespace}.php` file into a sibling
|
|
47
414
|
* `lang/{locale}/{namespace}.json` file, one per PHP file, so each PHP file
|
|
48
415
|
* becomes its own i18next namespace instead of being merged with the others.
|
|
416
|
+
* The PHP file keeps Laravel's conventions (`:name` placeholders, `one|many`
|
|
417
|
+
* plural forms); the JSON is written the i18next way (`{{name}}`,
|
|
418
|
+
* `key_one` / `key_other`), see `src/plugin/convert.ts`.
|
|
49
419
|
* A PHP file may sit at any depth under the locale directory --
|
|
50
420
|
* `lang/{locale}/{ns1}/{ns2}.php` and deeper -- and gets its sibling `.json`
|
|
51
421
|
* written at that same nested path; the resulting namespace name itself is
|
|
@@ -74,7 +444,8 @@ var locale = {
|
|
|
74
444
|
*/ function generateNamespaceFiles(localeDirname) {
|
|
75
445
|
return walkPhpFiles(localeDirname).sort().map((phpPath)=>{
|
|
76
446
|
const content = fs.readFileSync(phpPath, 'utf-8');
|
|
77
|
-
const
|
|
447
|
+
const locale = path.basename(localeDirname);
|
|
448
|
+
const translations = convertToI18next(convertToDottedKey(fromString(content)), locale);
|
|
78
449
|
const outputPath = phpPath.replace(/\.php$/, '.json');
|
|
79
450
|
fs.writeFileSync(outputPath, JSON.stringify(translations));
|
|
80
451
|
return {
|
|
@@ -98,6 +469,14 @@ var locale = {
|
|
|
98
469
|
] : [];
|
|
99
470
|
});
|
|
100
471
|
}
|
|
472
|
+
/**
|
|
473
|
+
* Rewrite every flattened Laravel message the i18next way for a language.
|
|
474
|
+
*
|
|
475
|
+
* @param source
|
|
476
|
+
* @param locale
|
|
477
|
+
*/ function convertToI18next(source, locale) {
|
|
478
|
+
return Object.entries(source).reduce((target, [key, value])=>Object.assign(target, convertEntry(key, value, locale)), {});
|
|
479
|
+
}
|
|
101
480
|
/**
|
|
102
481
|
* Flatten nested translations into dotted keys, e.g. `sub_level1.sub_level2.text`.
|
|
103
482
|
*
|
|
@@ -129,7 +508,7 @@ function countWildcardSegments(normalizedPattern) {
|
|
|
129
508
|
* Every directory an ordered source `pattern` currently expands to, relative
|
|
130
509
|
* to `root`. A pattern with no `*` is a single literal directory (existing or
|
|
131
510
|
* not -- callers decide what an absent literal directory means). A pattern
|
|
132
|
-
* with exactly one `*` segment (e.g. `
|
|
511
|
+
* with exactly one `*` segment (e.g. `packages/*\/lang`) is expanded against
|
|
133
512
|
* the filesystem: every existing subdirectory of the prefix before the `*`,
|
|
134
513
|
* with the suffix after it appended, kept only when the full path also
|
|
135
514
|
* exists, sorted. Two or more `*` segments throw -- unsupported.
|
|
@@ -162,8 +541,8 @@ function countWildcardSegments(normalizedPattern) {
|
|
|
162
541
|
* so this is always root-relative (a leading `/`). A `*` segment in the
|
|
163
542
|
* pattern passes through `path.resolve`/`path.relative`/`normalizePath`
|
|
164
543
|
* completely untouched -- none of them treat it specially -- which is why
|
|
165
|
-
* the emitted glob needs no separate wildcard handling: `
|
|
166
|
-
* becomes `/
|
|
544
|
+
* the emitted glob needs no separate wildcard handling: `packages/*\/lang`
|
|
545
|
+
* becomes `/packages/*\/lang/**\/*.json` and `import.meta.glob()` expands the
|
|
167
546
|
* `*` itself, natively, at both dev and build time.
|
|
168
547
|
*
|
|
169
548
|
* @param pattern
|
|
@@ -183,6 +562,10 @@ function countWildcardSegments(normalizedPattern) {
|
|
|
183
562
|
return dirnames.some((dir)=>normalizedFile.startsWith(`${normalizePath(dir)}/`));
|
|
184
563
|
}
|
|
185
564
|
|
|
565
|
+
/**
|
|
566
|
+
* The modules the namespace declaration is added to: the app's own scripts and
|
|
567
|
+
* single-file components, never dependencies.
|
|
568
|
+
*/ const SCRIPT_MODULE = /\.(?:[cm]?[jt]sx?|vue|svelte)$/;
|
|
186
569
|
/**
|
|
187
570
|
* Language files, one namespace per language file: loaded eagerly by server
|
|
188
571
|
* code (SSR), so it renders translated, and lazily by client code. The
|
|
@@ -194,13 +577,15 @@ const RESOLVED_FILES_MODULE_ID = `\0${FILES_MODULE_ID}`;
|
|
|
194
577
|
* Makes Laravel PHP translations available to i18next by generating a sibling
|
|
195
578
|
* `.json` file next to each `.php` file, across one or more ordered sources.
|
|
196
579
|
*/ function i18n(config) {
|
|
197
|
-
var _a;
|
|
580
|
+
var _a, _b, _c;
|
|
198
581
|
const logger = createLogger('info', {
|
|
199
582
|
prefix: '[laravel-i18next]'
|
|
200
583
|
});
|
|
201
584
|
const sourcePatterns = (_a = config === null || config === void 0 ? void 0 : config.sources) !== null && _a !== void 0 ? _a : [
|
|
202
585
|
'lang'
|
|
203
586
|
];
|
|
587
|
+
const eagerOnServer = ((_b = config === null || config === void 0 ? void 0 : config.ssr) !== null && _b !== void 0 ? _b : 'eager') === 'eager';
|
|
588
|
+
const detect = (_c = config === null || config === void 0 ? void 0 : config.detectNamespaces) !== null && _c !== void 0 ? _c : true;
|
|
204
589
|
let root = process.cwd();
|
|
205
590
|
let generated = [];
|
|
206
591
|
let exitHandlersBound = false;
|
|
@@ -263,9 +648,26 @@ const RESOLVED_FILES_MODULE_ID = `\0${FILES_MODULE_ID}`;
|
|
|
263
648
|
if (id !== RESOLVED_FILES_MODULE_ID) return undefined;
|
|
264
649
|
// Globs of virtual modules must be relative to the root. Recursive, so
|
|
265
650
|
// each also matches namespaces nested any number of levels deep.
|
|
266
|
-
const entries = sourcePatterns.map((pattern)=>JSON.stringify(toGlob(pattern, root))).map((pattern)=>(options === null || options === void 0 ? void 0 : options.ssr) ? `import.meta.glob(${pattern}, { eager: true })` : `import.meta.glob(${pattern})`).join(', ');
|
|
651
|
+
const entries = sourcePatterns.map((pattern)=>JSON.stringify(toGlob(pattern, root))).map((pattern)=>(options === null || options === void 0 ? void 0 : options.ssr) && eagerOnServer ? `import.meta.glob(${pattern}, { eager: true })` : `import.meta.glob(${pattern})`).join(', ');
|
|
267
652
|
return `export default [${entries}];`;
|
|
268
653
|
},
|
|
654
|
+
transform (code, id) {
|
|
655
|
+
const file = id.split('?')[0];
|
|
656
|
+
if (!detect || id.charCodeAt(0) === 0 || file.includes('/node_modules/') || !SCRIPT_MODULE.test(file)) {
|
|
657
|
+
return undefined;
|
|
658
|
+
}
|
|
659
|
+
const namespaces = detectNamespaces(code);
|
|
660
|
+
if (namespaces.length === 0) return undefined;
|
|
661
|
+
// Self-contained (see `src/demand.ts` for why it imports nothing), and
|
|
662
|
+
// on the first line so no line of the module moves.
|
|
663
|
+
const registry = `globalThis[Symbol.for(${JSON.stringify(DECLARED_NAMESPACES_KEY)})]`;
|
|
664
|
+
const adds = namespaces.map((namespace)=>`.add(${JSON.stringify(namespace)})`).join('');
|
|
665
|
+
const declaration = `(${registry}||(${registry}=new Set))${adds};`;
|
|
666
|
+
return {
|
|
667
|
+
code: declaration + code,
|
|
668
|
+
map: null
|
|
669
|
+
};
|
|
670
|
+
},
|
|
269
671
|
config (userConfig) {
|
|
270
672
|
var _a;
|
|
271
673
|
// `config()` runs before `configResolved()`, so `root` is not yet the
|
package/dist/vue.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var I18NextVue = require('i18next-vue');
|
|
4
|
-
var createI18n = require('./shared/create-i18n-
|
|
4
|
+
var createI18n = require('./shared/create-i18n-DI7Eog6O.cjs');
|
|
5
5
|
require('i18next');
|
|
6
|
+
require('./shared/demand-sbjTXVnl.cjs');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Vue plugin providing an i18next instance translating Laravel language files
|
package/dist/vue.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import I18NextVue from 'i18next-vue';
|
|
2
2
|
export { useTranslation } from 'i18next-vue';
|
|
3
|
-
import { c as createI18n, s as syncDocumentLang } from './shared/create-i18n-
|
|
3
|
+
import { c as createI18n, s as syncDocumentLang } from './shared/create-i18n-DaZzs1DJ.mjs';
|
|
4
4
|
import 'i18next';
|
|
5
|
+
import './shared/demand-6YnTqSM4.mjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Vue plugin providing an i18next instance translating Laravel language files
|