@x18n/builtins 0.0.0-stage → 0.0.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.
@@ -0,0 +1,1096 @@
1
+ import { defaultTemplates } from "./extractors.mjs";
2
+ import { n as sfcFluentBlocks, t as sfcBlocks } from "./sfc-blocks-CQhgEeT6.mjs";
3
+ import { braces, colon, dollars, doubleBraces, fluent, icu, linked, nesting, percentBraces, placeholders, printf } from "./placeholders.mjs";
4
+ import { r as regexScopes } from "./regex-350J_An-.mjs";
5
+ import { isMessageLeaf } from "@x18n/core";
6
+ //#region src/presets/shared.ts
7
+ /** One plugin holding a library's call specs, regexes, namespace rules, defaults and extraction templates. */
8
+ function library(options) {
9
+ return {
10
+ name: `x18n:${options.name}`,
11
+ async config(draft, ctx) {
12
+ if (options.namespace) draft.namespace = {
13
+ separator: options.namespace.separator,
14
+ default: draft.namespace.default ?? options.namespace.default
15
+ };
16
+ if (options.resources) draft.resources = [...draft.resources, ...options.resources.filter((spec) => !draft.resources.some((r) => r.pattern === spec.pattern))];
17
+ if (options.keyStyle && draft.keyStyle === "auto" && !ctx.user.keyStyle) draft.keyStyle = options.keyStyle;
18
+ draft.usage = {
19
+ ...draft.usage,
20
+ calls: [...draft.usage.calls, ...options.calls ?? []],
21
+ regex: [...draft.usage.regex, ...options.regex ?? []],
22
+ derivedKeys: [...draft.usage.derivedKeys, ...options.derivedKeys ?? []]
23
+ };
24
+ await options.extra?.(draft);
25
+ },
26
+ localeFinders: options.finders,
27
+ normalizeKey: options.normalizeKey,
28
+ transformResource: options.transformResource,
29
+ translatePrompt: options.translatePrompt,
30
+ extractors: options.templates ? [{
31
+ id: options.name,
32
+ templates: options.templates
33
+ }] : void 0
34
+ };
35
+ }
36
+ function dependsOn(names, prefixes = []) {
37
+ return (ctx) => ctx.hasAnyDependency(names) || [...ctx.dependencies].some((dep) => prefixes.some((prefix) => dep.startsWith(prefix)));
38
+ }
39
+ /** Extensions of JS and TS sources. */
40
+ const scriptExtensions = [
41
+ "js",
42
+ "jsx",
43
+ "mjs",
44
+ "cjs",
45
+ "ts",
46
+ "tsx",
47
+ "mts",
48
+ "cts"
49
+ ];
50
+ //#endregion
51
+ //#region src/presets/angular.ts
52
+ const templates = [
53
+ "html",
54
+ "ts",
55
+ "js"
56
+ ];
57
+ function ngxTranslate() {
58
+ return {
59
+ name: "ngx-translate",
60
+ detect: dependsOn(["@ngx-translate/core"]),
61
+ plugins: [library({
62
+ name: "ngx-translate",
63
+ calls: [{ callee: [
64
+ "translate.instant",
65
+ "translate.get",
66
+ "translate.stream",
67
+ "translateService.instant",
68
+ "translateService.get",
69
+ "translateService.stream",
70
+ "_"
71
+ ] }],
72
+ regex: [{
73
+ extensions: templates,
74
+ patterns: [
75
+ "[`'\"]({key})[`'\"][\\s\\n]*\\|[\\s\\n]*translate\\b",
76
+ "[\\s\\n]translate>[\\s\\n]*({key})[\\s\\n]*</",
77
+ "\\[translate\\]=[\"']'({key})'[\"']",
78
+ "\\stranslate=[\"']({key})[\"']"
79
+ ]
80
+ }],
81
+ resources: [{ pattern: "src/assets/i18n/{locale}.json" }, { pattern: "src/assets/i18n/{locale}/{namespaces}.json" }],
82
+ templates: [{
83
+ template: "{{ '{key}' | translate }}",
84
+ kinds: ["html-text"]
85
+ }, {
86
+ template: "this.translate.instant('{key}'{args})",
87
+ kinds: ["js-string", "js-template"]
88
+ }]
89
+ }), placeholders(doubleBraces)]
90
+ };
91
+ }
92
+ function transloco() {
93
+ return {
94
+ name: "transloco",
95
+ detect: dependsOn(["@jsverse/transloco", "@ngneat/transloco"]),
96
+ plugins: [
97
+ library({
98
+ name: "transloco",
99
+ calls: [{ callee: [
100
+ "translate",
101
+ "translocoService.translate",
102
+ "transloco.translate",
103
+ "translocoService.selectTranslate",
104
+ "transloco.selectTranslate"
105
+ ] }],
106
+ regex: [{
107
+ extensions: templates,
108
+ patterns: [
109
+ "[`'\"]({key})[`'\"][\\s\\n]*\\|[\\s\\n]*transloco\\b",
110
+ "[^\\w\\d]t\\([\\s\\n]*['\"`]({key})['\"`]",
111
+ "[^*\\w\\d]transloco=['\"`]({key})['\"`]"
112
+ ]
113
+ }],
114
+ resources: [{ pattern: "src/assets/i18n/{locale}.json" }, { pattern: "src/assets/i18n/{namespaces}/{locale}.json" }],
115
+ templates: [{
116
+ template: "{{ '{key}' | transloco }}",
117
+ kinds: ["html-text"]
118
+ }, {
119
+ template: "translate('{key}'{args})",
120
+ kinds: ["js-string", "js-template"],
121
+ imports: ["import { translate } from '@jsverse/transloco'"]
122
+ }]
123
+ }),
124
+ regexScopes("\\*transloco=\"[^\"]*read:\\s*'([^']+)'", "transloco-read", {
125
+ as: "keyPrefix",
126
+ extensions: ["html"]
127
+ }),
128
+ placeholders(doubleBraces)
129
+ ]
130
+ };
131
+ }
132
+ //#endregion
133
+ //#region src/presets/nls.ts
134
+ const nlsFile = /^(?:(.*)\/)?package\.nls(?:\.([\w-]+))?\.json$/;
135
+ const bundleFile = /^(?:(.*)\/)?bundle\.l10n(?:\.([\w-]+))?\.json$/;
136
+ /** VS Code extension strings: `package.nls[.locale].json` and `l10n/bundle.l10n[.locale].json`. */
137
+ const nlsFinder = {
138
+ id: "nls",
139
+ async find(ctx) {
140
+ const paths = await ctx.host.glob(["**/package.nls*.json", "**/bundle.l10n*.json"], {
141
+ ignore: ["**/node_modules/**"],
142
+ gitignore: true
143
+ });
144
+ const out = [];
145
+ for (const path of paths) {
146
+ const nls = nlsFile.exec(path);
147
+ const bundle = nls ? void 0 : bundleFile.exec(path);
148
+ const match = nls ?? bundle;
149
+ if (!match) continue;
150
+ out.push({
151
+ path,
152
+ locale: match[2] ?? ctx.config.sourceLocale,
153
+ namespace: void 0,
154
+ format: "json"
155
+ });
156
+ }
157
+ return out;
158
+ }
159
+ };
160
+ //#endregion
161
+ //#region src/presets/js.ts
162
+ const vueI18nCalls = [{ callee: [
163
+ "$t",
164
+ "t",
165
+ "tc",
166
+ "$tc",
167
+ "te",
168
+ "$te",
169
+ "tm",
170
+ "$tm",
171
+ "i18n.t",
172
+ "i18n.global.t",
173
+ "$i18n.t",
174
+ "i18n.tc",
175
+ "i18n.te"
176
+ ] }, {
177
+ callee: "useI18n",
178
+ factory: true,
179
+ returns: [
180
+ "t",
181
+ "tc",
182
+ "te",
183
+ "tm"
184
+ ]
185
+ }];
186
+ function vueI18n() {
187
+ return {
188
+ name: "vue-i18n",
189
+ detect: dependsOn([
190
+ "vue-i18n",
191
+ "vuex-i18n",
192
+ "petite-vue-i18n",
193
+ "vue-i18n-bridge"
194
+ ], ["@intlify/"]),
195
+ plugins: [
196
+ library({
197
+ name: "vue-i18n",
198
+ calls: vueI18nCalls,
199
+ templates: [
200
+ {
201
+ template: "{{ $t('{key}'{args}) }}",
202
+ kinds: ["vue-template-text"]
203
+ },
204
+ {
205
+ template: ":{attr}=\"$t('{key}'{args})\"",
206
+ kinds: ["vue-attribute"]
207
+ },
208
+ {
209
+ template: "t('{key}'{args})",
210
+ kinds: ["js-string", "js-template"],
211
+ files: ["**/*.vue"],
212
+ imports: ["import { useI18n } from 'vue-i18n'", "const { t } = useI18n()"]
213
+ }
214
+ ]
215
+ }),
216
+ placeholders(braces, linked),
217
+ sfcBlocks()
218
+ ]
219
+ };
220
+ }
221
+ function nuxtI18n() {
222
+ return {
223
+ name: "nuxt-i18n",
224
+ detect: dependsOn(["@nuxtjs/i18n", "nuxt-i18n"]),
225
+ plugins: [...vueI18n().plugins, {
226
+ name: "x18n:nuxt-i18n",
227
+ async config(draft, ctx) {
228
+ const nuxtConfig = await ctx.host.readFile("nuxt.config.ts") ?? await ctx.host.readFile("nuxt.config.js");
229
+ const langDir = nuxtConfig ? /langDir:\s*['"]([^'"]+)['"]/.exec(nuxtConfig)?.[1]?.replace(/^\.\/|\/$/g, "") : void 0;
230
+ const dirs = langDir ? [`i18n/${langDir}`, langDir] : [
231
+ "i18n/locales",
232
+ "locales",
233
+ "lang"
234
+ ];
235
+ draft.resources = [...draft.resources, ...dirs.map((dir) => ({ pattern: `${dir}/{locale}.{ext}` }))];
236
+ }
237
+ }]
238
+ };
239
+ }
240
+ const i18nextPlugin = library({
241
+ name: "i18next",
242
+ namespace: {
243
+ separator: ":",
244
+ default: "translation"
245
+ },
246
+ calls: [
247
+ {
248
+ callee: [
249
+ "t",
250
+ "i18next.t",
251
+ "i18n.t"
252
+ ],
253
+ namespace: {
254
+ arg: 1,
255
+ property: "ns"
256
+ }
257
+ },
258
+ {
259
+ callee: [
260
+ "getFixedT",
261
+ "i18next.getFixedT",
262
+ "i18n.getFixedT"
263
+ ],
264
+ factory: true,
265
+ returns: ["t"],
266
+ namespace: { arg: 1 },
267
+ keyPrefix: { arg: 2 }
268
+ },
269
+ {
270
+ callee: "tr",
271
+ types: ["TFunction"]
272
+ }
273
+ ],
274
+ derivedKeys: ["{key}_*"],
275
+ translatePrompt: (draft, ctx) => {
276
+ draft.notes.push(`i18next plural keys end in _${ctx.localeInfo.pluralCategories.join(", _")} for ${ctx.targetLocale}; write one key per category.`);
277
+ },
278
+ templates: [{
279
+ template: "i18next.t('{key}'{args})",
280
+ kinds: ["js-string", "js-template"],
281
+ imports: ["import i18next from 'i18next'"]
282
+ }]
283
+ });
284
+ function i18next() {
285
+ return {
286
+ name: "i18next",
287
+ detect: dependsOn(["i18next"]),
288
+ plugins: [i18nextPlugin, placeholders(doubleBraces, nesting)]
289
+ };
290
+ }
291
+ function reactI18next() {
292
+ return {
293
+ name: "react-i18next",
294
+ detect: dependsOn(["react-i18next", "next-i18next"]),
295
+ plugins: [
296
+ i18nextPlugin,
297
+ placeholders(doubleBraces, nesting),
298
+ library({
299
+ name: "react-i18next",
300
+ calls: [
301
+ {
302
+ callee: "useTranslation",
303
+ factory: true,
304
+ returns: ["t"],
305
+ namespace: { arg: 0 },
306
+ keyPrefix: {
307
+ arg: 1,
308
+ property: "keyPrefix"
309
+ }
310
+ },
311
+ {
312
+ callee: "Trans",
313
+ key: {
314
+ arg: 0,
315
+ property: "i18nKey"
316
+ },
317
+ namespace: {
318
+ arg: 0,
319
+ property: "ns"
320
+ }
321
+ },
322
+ {
323
+ callee: "Translation",
324
+ factory: true,
325
+ returns: ["t"],
326
+ namespace: {
327
+ arg: 0,
328
+ property: "ns"
329
+ }
330
+ }
331
+ ],
332
+ templates: [{
333
+ template: "<Trans i18nKey=\"{key}\" />",
334
+ kinds: ["jsx-text"],
335
+ imports: ["import { Trans } from 'react-i18next'"]
336
+ }, {
337
+ template: "{attr}={i18next.t('{key}'{args})}",
338
+ kinds: ["jsx-attribute"],
339
+ imports: ["import i18next from 'i18next'"]
340
+ }]
341
+ })
342
+ ]
343
+ };
344
+ }
345
+ function nextIntl() {
346
+ return {
347
+ name: "next-intl",
348
+ detect: dependsOn(["next-intl", "use-intl"]),
349
+ plugins: [library({
350
+ name: "next-intl",
351
+ calls: [{
352
+ callee: ["useTranslations", "getTranslations"],
353
+ factory: true,
354
+ returns: [
355
+ "t",
356
+ "t.rich",
357
+ "t.markup",
358
+ "t.raw",
359
+ "t.has"
360
+ ],
361
+ keyPrefix: { arg: 0 }
362
+ }, {
363
+ callee: "getTranslations",
364
+ factory: true,
365
+ returns: [
366
+ "t",
367
+ "t.rich",
368
+ "t.markup",
369
+ "t.raw",
370
+ "t.has"
371
+ ],
372
+ keyPrefix: {
373
+ arg: 0,
374
+ property: "namespace"
375
+ }
376
+ }],
377
+ resources: [{ pattern: "messages/{locale}.json" }, { pattern: "src/messages/{locale}.json" }],
378
+ templates: [
379
+ {
380
+ template: "{t('{key}'{args})}",
381
+ kinds: ["jsx-text"]
382
+ },
383
+ {
384
+ template: "{attr}={t('{key}'{args})}",
385
+ kinds: ["jsx-attribute"]
386
+ },
387
+ {
388
+ template: "t('{key}'{args})",
389
+ kinds: ["js-string", "js-template"]
390
+ }
391
+ ]
392
+ }), placeholders(icu)]
393
+ };
394
+ }
395
+ function reactIntl() {
396
+ return {
397
+ name: "react-intl",
398
+ detect: dependsOn(["react-intl"], ["@formatjs/"]),
399
+ plugins: [library({
400
+ name: "react-intl",
401
+ calls: [{
402
+ callee: [
403
+ "intl.formatMessage",
404
+ "formatMessage",
405
+ "defineMessage",
406
+ "FormattedMessage"
407
+ ],
408
+ key: {
409
+ arg: 0,
410
+ property: "id"
411
+ }
412
+ }, {
413
+ callee: "defineMessages",
414
+ key: {
415
+ arg: 0,
416
+ property: "*.id"
417
+ }
418
+ }],
419
+ templates: [
420
+ {
421
+ template: "<FormattedMessage id=\"{key}\" />",
422
+ kinds: ["jsx-text"],
423
+ imports: ["import { FormattedMessage } from 'react-intl'"]
424
+ },
425
+ {
426
+ template: "{attr}={intl.formatMessage({ id: '{key}' }{args})}",
427
+ kinds: ["jsx-attribute"]
428
+ },
429
+ {
430
+ template: "intl.formatMessage({ id: '{key}' }{args})",
431
+ kinds: ["js-string", "js-template"]
432
+ }
433
+ ]
434
+ }), placeholders(icu)]
435
+ };
436
+ }
437
+ function svelteI18n() {
438
+ return {
439
+ name: "svelte-i18n",
440
+ detect: dependsOn([
441
+ "svelte-i18n",
442
+ "sveltekit-i18n",
443
+ "@sveltekit-i18n/base"
444
+ ]),
445
+ plugins: [library({
446
+ name: "svelte-i18n",
447
+ calls: [{ callee: [
448
+ "$_",
449
+ "$t",
450
+ "$format",
451
+ "_",
452
+ "t",
453
+ "format",
454
+ "$json",
455
+ "json"
456
+ ] }],
457
+ templates: [{
458
+ template: "{$_('{key}'{args})}",
459
+ kinds: ["svelte-text"],
460
+ imports: ["import { _ } from 'svelte-i18n'"]
461
+ }, {
462
+ template: "{attr}={$_('{key}'{args})}",
463
+ kinds: ["svelte-attribute"],
464
+ imports: ["import { _ } from 'svelte-i18n'"]
465
+ }]
466
+ }), placeholders(icu)]
467
+ };
468
+ }
469
+ function vscodeExtension() {
470
+ return {
471
+ name: "vscode-extension",
472
+ detect: (ctx) => ctx.hasAnyDependency(["vscode", "@types/vscode"]),
473
+ plugins: [library({
474
+ name: "vscode-extension",
475
+ calls: [{ callee: ["l10n.t", "vscode.l10n.t"] }],
476
+ finders: [nlsFinder],
477
+ extra: (draft) => {
478
+ draft.usage = {
479
+ ...draft.usage,
480
+ regex: [...draft.usage.regex, {
481
+ extensions: ["json"],
482
+ patterns: ["\"%({key})%\""]
483
+ }]
484
+ };
485
+ }
486
+ }), placeholders(braces)]
487
+ };
488
+ }
489
+ /** Always last: hard-string extraction templates for plain JS, TS and HTML. */
490
+ function general() {
491
+ return {
492
+ name: "general",
493
+ detect: () => true,
494
+ plugins: [{
495
+ name: "x18n:general",
496
+ extractors: [{
497
+ id: "general",
498
+ templates: defaultTemplates
499
+ }]
500
+ }]
501
+ };
502
+ }
503
+ //#endregion
504
+ //#region src/presets/mobile.ts
505
+ /** Flutter with `flutter_i18n`: JSON or YAML assets, `FlutterI18n.translate(context, 'key')`. */
506
+ function flutter() {
507
+ return {
508
+ name: "flutter",
509
+ detect: dependsOn(["flutter_i18n"]),
510
+ plugins: [library({
511
+ name: "flutter",
512
+ regex: [{
513
+ extensions: ["dart"],
514
+ patterns: ["[^\\w\\d]FlutterI18n\\.(?:plural|translate)\\(\\s*[\\w\\d.]+,\\s*['\"`]({key})['\"`]", "[^\\w\\d]I18n(?:Plural|Text)\\(\\s*['\"`]({key})['\"`]"]
515
+ }],
516
+ resources: [{ pattern: "assets/flutter_i18n/{locale}.{ext}" }, { pattern: "assets/i18n/{locale}.{ext}" }],
517
+ templates: [{ template: "FlutterI18n.translate(context, '{key}')" }]
518
+ }), placeholders(braces)]
519
+ };
520
+ }
521
+ //#endregion
522
+ //#region src/presets/server.ts
523
+ /** Laravel: `lang/{locale}/{file}.php` arrays and `lang/{locale}.json`; keys are `file.key`. Exclusive like i18n-ally. */
524
+ function laravel() {
525
+ return {
526
+ name: "laravel",
527
+ detect: dependsOn(["laravel/framework"]),
528
+ exclusive: true,
529
+ plugins: [library({
530
+ name: "laravel",
531
+ regex: [{
532
+ extensions: ["php"],
533
+ patterns: ["[^\\w\\d](?:__|trans|@lang|trans_choice|Lang::get|Lang::choice)\\(\\s*'([^']+)'", "[^\\w\\d](?:__|trans|@lang|trans_choice|Lang::get|Lang::choice)\\(\\s*\"([^\"]+)\""]
534
+ }],
535
+ resources: [
536
+ { pattern: "lang/{locale}/{namespaces}.php" },
537
+ { pattern: "resources/lang/{locale}/{namespaces}.php" },
538
+ { pattern: "lang/{locale}.json" },
539
+ { pattern: "resources/lang/{locale}.json" }
540
+ ],
541
+ normalizeKey: (raw) => raw.includes("/") ? raw.replace(/\//g, ".") : void 0,
542
+ templates: [{ template: "__('{key}')" }]
543
+ }), placeholders(colon)]
544
+ };
545
+ }
546
+ /** Joomla: INI language files, `Text::_('KEY')`. */
547
+ function phpJoomla() {
548
+ return {
549
+ name: "php-joomla",
550
+ detect: dependsOn(["joomla/application", "joomla/cms"]),
551
+ plugins: [library({
552
+ name: "php-joomla",
553
+ regex: [{
554
+ extensions: ["php"],
555
+ patterns: ["J?Text::(?:_|[\\w]+)\\(\\s*['\"]({key})['\"]"]
556
+ }],
557
+ resources: [{ pattern: "language/{locale}/*.ini" }, { pattern: "administrator/language/{locale}/*.ini" }],
558
+ keyStyle: "flat",
559
+ templates: [{ template: "Text::_('{key}')" }]
560
+ }), placeholders(printf)]
561
+ };
562
+ }
563
+ /** PHP gettext with `.po` files. Exclusive and never detected, as in i18n-ally: list it in `presets`. */
564
+ function phpGettext() {
565
+ return {
566
+ name: "php-gettext",
567
+ exclusive: true,
568
+ plugins: [library({
569
+ name: "php-gettext",
570
+ regex: [{
571
+ extensions: ["php"],
572
+ patterns: ["[^\\w\\d](?:gettext|_|__)\\(\\s*['\"`]({key})['\"`]"]
573
+ }],
574
+ resources: [{ pattern: "locale/{locale}/LC_MESSAGES/*.po" }, { pattern: "locales/{locale}.po" }],
575
+ extra: (draft) => {
576
+ draft.extract = {
577
+ keygen: "source",
578
+ ...draft.extract
579
+ };
580
+ },
581
+ templates: [{ template: "gettext('{key}')" }]
582
+ }), placeholders(printf)]
583
+ };
584
+ }
585
+ const viewSuffix = /(?:\.html)?\.(?:erb|haml|slim)$/;
586
+ /** `t('.title')` in `app/views/users/index.html.erb` means `users.index.title`. */
587
+ function railsLazyKey(raw, path) {
588
+ if (!raw.startsWith(".")) return void 0;
589
+ const match = /(?:^|\/)app\/views\/(.+)$/.exec(path);
590
+ if (!match) return void 0;
591
+ return `${match[1].replace(viewSuffix, "").split("/").map((segment) => segment.replace(/^_/, "")).join(".")}${raw}`;
592
+ }
593
+ function rubyRails() {
594
+ return {
595
+ name: "ruby-rails",
596
+ detect: dependsOn([
597
+ "rails",
598
+ "rails-i18n",
599
+ "railties"
600
+ ]),
601
+ plugins: [library({
602
+ name: "ruby-rails",
603
+ regex: [{
604
+ extensions: [
605
+ "rb",
606
+ "erb",
607
+ "haml",
608
+ "slim"
609
+ ],
610
+ patterns: [
611
+ "[^\\w\\d]t(?:\\(| )\\s*['\"`]({key})['\"`]",
612
+ "[^\\w\\d]t\\(:({key})\\)",
613
+ "I18n\\.t\\(\\s*['\"`]({key})['\"`]"
614
+ ]
615
+ }],
616
+ resources: [
617
+ { pattern: "config/locales/{locale}.yml" },
618
+ { pattern: "config/locales/**/*.{locale}.yml" },
619
+ { pattern: "config/locales/{locale}/**/*.yml" }
620
+ ],
621
+ normalizeKey: (raw, ctx) => railsLazyKey(raw, ctx.path),
622
+ transformResource: {
623
+ applies: (ctx) => /\.ya?ml$/.test(ctx.path),
624
+ read(tree, ctx) {
625
+ const root = tree[ctx.locale];
626
+ return root !== void 0 && !isMessageLeaf(root) ? root : {};
627
+ },
628
+ toStored: (path, ctx) => [ctx.locale, ...path]
629
+ },
630
+ templates: [{ template: "t('{key}')" }]
631
+ }), placeholders(percentBraces)]
632
+ };
633
+ }
634
+ function jekyll() {
635
+ return {
636
+ name: "jekyll",
637
+ detect: dependsOn(["jekyll-multiple-languages-plugin"]),
638
+ plugins: [library({
639
+ name: "jekyll",
640
+ regex: [{
641
+ extensions: [
642
+ "html",
643
+ "md",
644
+ "liquid"
645
+ ],
646
+ patterns: ["\\{%\\s+t\\s+({key})\\s+%\\}"]
647
+ }],
648
+ resources: [{ pattern: "_i18n/{locale}.yml" }],
649
+ keyStyle: "nested",
650
+ templates: [{ template: "{% t {key} %}" }]
651
+ })]
652
+ };
653
+ }
654
+ //#endregion
655
+ //#region src/presets/web.ts
656
+ const jsxTemplates = [
657
+ {
658
+ template: "{t('{key}'{args})}",
659
+ kinds: ["jsx-text"]
660
+ },
661
+ {
662
+ template: "{attr}={t('{key}'{args})}",
663
+ kinds: ["jsx-attribute"]
664
+ },
665
+ {
666
+ template: "t('{key}'{args})",
667
+ kinds: ["js-string", "js-template"]
668
+ }
669
+ ];
670
+ function nextInternational() {
671
+ return {
672
+ name: "next-international",
673
+ detect: dependsOn(["next-international"]),
674
+ plugins: [library({
675
+ name: "next-international",
676
+ calls: [{
677
+ callee: ["useI18n", "getI18n"],
678
+ factory: true,
679
+ returns: ["t"]
680
+ }, {
681
+ callee: ["useScopedI18n", "getScopedI18n"],
682
+ factory: true,
683
+ returns: ["t"],
684
+ keyPrefix: { arg: 0 }
685
+ }],
686
+ resources: [{ pattern: "locales/{locale}.{ext}" }, { pattern: "src/locales/{locale}.{ext}" }],
687
+ templates: jsxTemplates
688
+ }), placeholders(braces)]
689
+ };
690
+ }
691
+ function nextTranslate() {
692
+ return {
693
+ name: "next-translate",
694
+ detect: dependsOn(["next-translate"]),
695
+ plugins: [library({
696
+ name: "next-translate",
697
+ namespace: {
698
+ separator: ":",
699
+ default: "common"
700
+ },
701
+ calls: [
702
+ {
703
+ callee: "useTranslation",
704
+ factory: true,
705
+ returns: ["t"],
706
+ namespace: { arg: 0 }
707
+ },
708
+ {
709
+ callee: "getT",
710
+ factory: true,
711
+ returns: ["t"],
712
+ namespace: { arg: 1 }
713
+ },
714
+ {
715
+ callee: "Trans",
716
+ key: {
717
+ arg: 0,
718
+ property: "i18nKey"
719
+ },
720
+ namespace: {
721
+ arg: 0,
722
+ property: "ns"
723
+ }
724
+ },
725
+ { callee: "t" }
726
+ ],
727
+ regex: [{
728
+ extensions: scriptExtensions,
729
+ patterns: ["[^\\w\\d]t`({key})`"]
730
+ }],
731
+ resources: [{ pattern: "locales/{locale}/{namespace}.json" }],
732
+ keyStyle: "nested",
733
+ templates: jsxTemplates
734
+ }), placeholders(doubleBraces)]
735
+ };
736
+ }
737
+ /** Shopify's i18next format: plural and ordinal forms as nested keys (`{key}.one`). */
738
+ function i18nextShopify() {
739
+ const forms = [
740
+ "plural",
741
+ "zero",
742
+ "one",
743
+ "two",
744
+ "few",
745
+ "many",
746
+ "other",
747
+ ...Array.from({ length: 10 }, (_, i) => String(i))
748
+ ];
749
+ return {
750
+ name: "i18next-shopify",
751
+ detect: dependsOn(["@shopify/i18next-shopify"]),
752
+ plugins: [
753
+ ...reactI18next().plugins,
754
+ library({
755
+ name: "i18next-shopify",
756
+ derivedKeys: forms.map((form) => `{key}.${form}`),
757
+ keyStyle: "nested"
758
+ }),
759
+ placeholders(braces)
760
+ ]
761
+ };
762
+ }
763
+ function i18nTag() {
764
+ return {
765
+ name: "i18n-tag",
766
+ detect: dependsOn(["es2015-i18n-tag"]),
767
+ plugins: [library({
768
+ name: "i18n-tag",
769
+ regex: [{
770
+ extensions: [...scriptExtensions, "vue"],
771
+ patterns: ["i18n`({key})`"]
772
+ }],
773
+ templates: [{
774
+ template: "i18n`{key}`",
775
+ kinds: ["js-string", "js-template"]
776
+ }]
777
+ })]
778
+ };
779
+ }
780
+ function fluentVue() {
781
+ return {
782
+ name: "fluent-vue",
783
+ detect: dependsOn([
784
+ "fluent-vue",
785
+ "fluent-vue-loader",
786
+ "rollup-plugin-fluent-vue",
787
+ "unplugin-fluent-vue"
788
+ ]),
789
+ plugins: [
790
+ library({
791
+ name: "fluent-vue",
792
+ calls: [{ callee: [
793
+ "$t",
794
+ "$ta",
795
+ "fluent.format",
796
+ "fluent.formatAttrs",
797
+ "this.$t",
798
+ "this.$ta"
799
+ ] }, {
800
+ callee: "useFluent",
801
+ factory: true,
802
+ returns: [
803
+ "$t",
804
+ "$ta",
805
+ "format",
806
+ "formatAttrs"
807
+ ]
808
+ }],
809
+ regex: [{
810
+ extensions: ["vue"],
811
+ patterns: ["v-t:({key})[\\s\\n=>]", "<i18n[\\s\\n]+path=['\"`]({key})['\"`]"]
812
+ }],
813
+ resources: [{ pattern: "locales/{locale}.ftl" }, { pattern: "src/locales/{locale}.ftl" }],
814
+ templates: [
815
+ {
816
+ template: "{{ $t('{key}'{args}) }}",
817
+ kinds: ["vue-template-text"]
818
+ },
819
+ {
820
+ template: ":{attr}=\"$t('{key}'{args})\"",
821
+ kinds: ["vue-attribute"]
822
+ },
823
+ {
824
+ template: "$t('{key}'{args})",
825
+ kinds: ["js-string", "js-template"]
826
+ }
827
+ ]
828
+ }),
829
+ sfcFluentBlocks(),
830
+ placeholders(fluent)
831
+ ]
832
+ };
833
+ }
834
+ function polyglot() {
835
+ return {
836
+ name: "polyglot",
837
+ detect: dependsOn(["node-polyglot"]),
838
+ plugins: [library({
839
+ name: "polyglot",
840
+ calls: [{ callee: ["polyglot.t", "I18n.t"] }],
841
+ regex: [{
842
+ extensions: ["rb"],
843
+ patterns: ["(?:polyglot|I18n)\\.t\\(\\s*['\"`]({key})['\"`]"]
844
+ }],
845
+ templates: [{
846
+ template: "polyglot.t('{key}'{args})",
847
+ kinds: ["js-string", "js-template"]
848
+ }]
849
+ }), placeholders(percentBraces)]
850
+ };
851
+ }
852
+ function globalize() {
853
+ return {
854
+ name: "globalize",
855
+ detect: dependsOn(["globalize"]),
856
+ plugins: [library({
857
+ name: "globalize",
858
+ calls: [{ callee: [
859
+ "Globalize.messageFormatter",
860
+ "Globalize.formatMessage",
861
+ "globalize.messageFormatter",
862
+ "globalize.formatMessage"
863
+ ] }],
864
+ normalizeKey: (raw) => raw.includes("/") ? raw.replace(/\//g, ".") : void 0,
865
+ templates: [{
866
+ template: "Globalize.formatMessage('{key}'{args})",
867
+ kinds: ["js-string", "js-template"]
868
+ }]
869
+ }), placeholders(icu)]
870
+ };
871
+ }
872
+ function ui5() {
873
+ return {
874
+ name: "ui5",
875
+ detect: dependsOn(["@ui5/cli"]),
876
+ plugins: [library({
877
+ name: "ui5",
878
+ regex: [{
879
+ extensions: [
880
+ "xml",
881
+ "json",
882
+ ...scriptExtensions
883
+ ],
884
+ patterns: ["\\{i18n>({key})\\}"]
885
+ }, {
886
+ extensions: scriptExtensions,
887
+ patterns: ["getResourceBundle\\(\\)\\.getText\\(\\s*['\"`]({key})['\"`]"]
888
+ }],
889
+ resources: [{ pattern: "webapp/i18n/i18n_{locale}.properties" }, { pattern: "webapp/i18n/i18n.properties" }],
890
+ keyStyle: "flat",
891
+ templates: [{
892
+ template: "{i18n>{key}}",
893
+ files: ["**/*.xml"]
894
+ }, {
895
+ template: "this.getResourceBundle().getText('{key}')",
896
+ kinds: ["js-string", "js-template"]
897
+ }]
898
+ }), placeholders(braces)]
899
+ };
900
+ }
901
+ function lingui() {
902
+ return {
903
+ name: "lingui",
904
+ detect: dependsOn([
905
+ "@lingui/core",
906
+ "@lingui/react",
907
+ "@lingui/macro"
908
+ ]),
909
+ plugins: [library({
910
+ name: "lingui",
911
+ calls: [
912
+ { callee: [
913
+ "i18n._",
914
+ "i18n.t",
915
+ "t",
916
+ "_"
917
+ ] },
918
+ {
919
+ callee: [
920
+ "t",
921
+ "msg",
922
+ "defineMessage",
923
+ "i18n._",
924
+ "Trans"
925
+ ],
926
+ key: {
927
+ arg: 0,
928
+ property: "id"
929
+ }
930
+ },
931
+ {
932
+ callee: "useLingui",
933
+ factory: true,
934
+ returns: ["t", "_"]
935
+ }
936
+ ],
937
+ regex: [{
938
+ extensions: scriptExtensions,
939
+ patterns: ["\\bt`({key})`", "<Trans>[\\s]*({key})[\\s]*<\\/Trans>"]
940
+ }],
941
+ resources: [{ pattern: "src/locales/{locale}/messages.po" }, { pattern: "locales/{locale}/messages.po" }],
942
+ extra: (draft) => {
943
+ draft.extract = {
944
+ keygen: "source",
945
+ ...draft.extract
946
+ };
947
+ },
948
+ templates: [{
949
+ template: "<Trans>{key}</Trans>",
950
+ kinds: ["jsx-text"],
951
+ imports: ["import { Trans } from '@lingui/react/macro'"]
952
+ }, {
953
+ template: "t`{key}`",
954
+ kinds: ["js-string", "js-template"],
955
+ imports: ["import { t } from '@lingui/core/macro'"]
956
+ }]
957
+ }), placeholders(icu)]
958
+ };
959
+ }
960
+ function emberIntl() {
961
+ return {
962
+ name: "ember-intl",
963
+ detect: dependsOn(["ember-intl"]),
964
+ plugins: [library({
965
+ name: "ember-intl",
966
+ calls: [{ callee: ["intl.t"] }],
967
+ regex: [{
968
+ extensions: [
969
+ "hbs",
970
+ "gjs",
971
+ "gts"
972
+ ],
973
+ patterns: ["\\{\\{\\s*t\\s+['\"`]({key})['\"`]", "\\(\\s*t\\s+['\"`]({key})['\"`]"]
974
+ }],
975
+ resources: [{ pattern: "translations/{locale}.{ext}" }, { pattern: "translations/{namespaces}/{locale}.{ext}" }],
976
+ templates: [{
977
+ template: "{{t '{key}'}}",
978
+ files: ["**/*.hbs"]
979
+ }, {
980
+ template: "this.intl.t('{key}'{args})",
981
+ kinds: ["js-string", "js-template"]
982
+ }]
983
+ }), placeholders(icu)]
984
+ };
985
+ }
986
+ function i18nJs() {
987
+ return {
988
+ name: "i18n-js",
989
+ detect: dependsOn(["i18n-js"]),
990
+ plugins: [library({
991
+ name: "i18n-js",
992
+ calls: [{ callee: ["I18n.t", "i18n.t"] }],
993
+ templates: [{
994
+ template: "i18n.t('{key}'{args})",
995
+ kinds: ["js-string", "js-template"]
996
+ }]
997
+ }), placeholders(percentBraces)]
998
+ };
999
+ }
1000
+ /** Chrome and WebExtension `_locales/{locale}/messages.json`: `{ "name": { "message": "..." } }` reads as `name`. */
1001
+ function chromeExtension() {
1002
+ return {
1003
+ name: "chrome-extension",
1004
+ async detect(ctx) {
1005
+ for (const path of [
1006
+ "manifest.json",
1007
+ "src/manifest.json",
1008
+ "public/manifest.json"
1009
+ ]) if ((await ctx.host.readFile(path))?.includes("\"default_locale\"")) return true;
1010
+ return false;
1011
+ },
1012
+ plugins: [
1013
+ library({
1014
+ name: "chrome-extension",
1015
+ calls: [{ callee: [
1016
+ "chrome.i18n.getMessage",
1017
+ "browser.i18n.getMessage",
1018
+ "i18n.getMessage"
1019
+ ] }],
1020
+ regex: [{
1021
+ extensions: [
1022
+ "json",
1023
+ "html",
1024
+ "css"
1025
+ ],
1026
+ patterns: ["__MSG_({key})__"]
1027
+ }],
1028
+ resources: [{ pattern: "_locales/{locale}/messages.json" }, { pattern: "{src,public,extension}/_locales/{locale}/messages.json" }],
1029
+ transformResource: {
1030
+ applies: (ctx) => /(?:^|\/)_locales\/[^/]+\/messages\.json$/.test(ctx.path),
1031
+ read(tree) {
1032
+ const out = {};
1033
+ for (const [name, entry] of Object.entries(tree)) {
1034
+ const message = isMessageLeaf(entry) ? void 0 : entry.message;
1035
+ if (message !== void 0 && isMessageLeaf(message)) out[name] = message;
1036
+ }
1037
+ return out;
1038
+ },
1039
+ toStored: (path) => [path.join("."), "message"]
1040
+ },
1041
+ templates: [{
1042
+ template: "chrome.i18n.getMessage('{key}')",
1043
+ kinds: ["js-string", "js-template"]
1044
+ }]
1045
+ }),
1046
+ {
1047
+ name: "x18n:chrome-extension-source-locale",
1048
+ async config(draft, ctx) {
1049
+ const manifest = await ctx.host.readFile("manifest.json") ?? await ctx.host.readFile("src/manifest.json") ?? await ctx.host.readFile("public/manifest.json");
1050
+ const locale = manifest ? /"default_locale"\s*:\s*"([^"]+)"/.exec(manifest)?.[1] : void 0;
1051
+ if (locale) draft.sourceLocale = locale;
1052
+ }
1053
+ },
1054
+ placeholders(dollars)
1055
+ ]
1056
+ };
1057
+ }
1058
+ //#endregion
1059
+ //#region src/presets/index.ts
1060
+ /**
1061
+ * Presets by name, in detection order: every i18n-ally framework has one.
1062
+ * Presets that share plugins (react-i18next and i18next) apply them once.
1063
+ */
1064
+ const presets = {
1065
+ "nuxt-i18n": () => nuxtI18n(),
1066
+ "vue-i18n": () => vueI18n(),
1067
+ "fluent-vue": () => fluentVue(),
1068
+ "i18next-shopify": () => i18nextShopify(),
1069
+ "react-i18next": () => reactI18next(),
1070
+ "next-translate": () => nextTranslate(),
1071
+ "next-international": () => nextInternational(),
1072
+ "i18next": () => i18next(),
1073
+ "next-intl": () => nextIntl(),
1074
+ "react-intl": () => reactIntl(),
1075
+ "svelte-i18n": () => svelteI18n(),
1076
+ "ngx-translate": () => ngxTranslate(),
1077
+ "transloco": () => transloco(),
1078
+ "lingui": () => lingui(),
1079
+ "ember-intl": () => emberIntl(),
1080
+ "polyglot": () => polyglot(),
1081
+ "globalize": () => globalize(),
1082
+ "i18n-tag": () => i18nTag(),
1083
+ "i18n-js": () => i18nJs(),
1084
+ "ui5": () => ui5(),
1085
+ "chrome-extension": () => chromeExtension(),
1086
+ "vscode-extension": () => vscodeExtension(),
1087
+ "laravel": () => laravel(),
1088
+ "php-joomla": () => phpJoomla(),
1089
+ "php-gettext": () => phpGettext(),
1090
+ "ruby-rails": () => rubyRails(),
1091
+ "jekyll": () => jekyll(),
1092
+ "flutter": () => flutter(),
1093
+ "general": () => general()
1094
+ };
1095
+ //#endregion
1096
+ export { ngxTranslate as A, nextIntl as C, svelteI18n as D, reactIntl as E, vscodeExtension as O, i18next as S, reactI18next as T, phpJoomla as _, globalize as a, flutter as b, i18nextShopify as c, nextTranslate as d, polyglot as f, phpGettext as g, laravel as h, fluentVue as i, transloco as j, vueI18n as k, lingui as l, jekyll as m, chromeExtension as n, i18nJs as o, ui5 as p, emberIntl as r, i18nTag as s, presets as t, nextInternational as u, railsLazyKey as v, nuxtI18n as w, general as x, rubyRails as y };