vite-plugin-drupal-t 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Märt Rang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # Drupal translations extract plugin for Vite
2
+
3
+ A Vite plugin that automatically extracts `Drupal.t()` and `Drupal.formatPlural()` translation calls from your JavaScript/TypeScript source code into a separate file. This enables Drupal's translation system to discover and process strings from your Vite-built frontend applications, making internationalization seamless across your entire stack.
4
+
5
+ The plugin scans your codebase during the build process, collects all translation calls, and outputs them as JavaScript comments in a `translations.js` file. Once included in your Drupal library, these strings become available for translation through Drupal's standard translation interface.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install vite-plugin-drupal-t --save-dev
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Add to **vite.config.ts** file:
16
+ ```ts
17
+ import { defineConfig } from 'vite'
18
+ import extractDrupalT from 'vite-plugin-drupal-t'
19
+
20
+ export default defineConfig({
21
+ plugins: [
22
+ extractDrupalT({
23
+ include: ['**/*.js', '**/*.ts', '**/*.vue'],
24
+ }),
25
+ ],
26
+ })
27
+ ```
28
+
29
+ Or if you need it only during build:
30
+
31
+ ```ts
32
+ import { defineConfig } from 'vite'
33
+ import extractDrupalT from 'vite-plugin-drupal-t'
34
+
35
+ export default defineConfig({
36
+ build: {
37
+ rollupOptions: {
38
+ plugins: [
39
+ extractDrupalT({
40
+ include: ['**/*.js', '**/*.ts', '**/*.vue'],
41
+ }),
42
+ ],
43
+ },
44
+ },
45
+ })
46
+ ```
47
+
48
+ ## Output
49
+
50
+ The plugin creates a `translations.js` file in your build output directory (e.g., `dist/translations.js`). This file contains all translation strings found in the codebase as JavaScript comments:
51
+
52
+ ```javascript
53
+ // Drupal.t('Welcome to the site')
54
+ // Drupal.t('Hello @name', {'@name': userName})
55
+ // Drupal.formatPlural(5, '1 item', '@count items', {})
56
+ ```
57
+
58
+ The translations are commented out, so they won't execute at runtime, but Drupal's string extraction system can still find and process them.
59
+
60
+ ## Drupal Setup
61
+
62
+ For Drupal to discover and extract these translation strings, you **must include the `translations.js` file in a Drupal library definition**.
63
+
64
+ ### Step 1: Add to your library definition
65
+
66
+ In your theme or module's `*.libraries.yml` file (e.g., `mytheme.libraries.yml`):
67
+
68
+ ```yaml
69
+ global:
70
+ js:
71
+ dist/translations.js: {}
72
+ dist/main.js: {}
73
+ css:
74
+ theme:
75
+ dist/style.css: {}
76
+ ```
77
+
78
+ Or create a dedicated library just for translations:
79
+
80
+ ```yaml
81
+ translations:
82
+ js:
83
+ dist/translations.js: {}
84
+
85
+ main:
86
+ js:
87
+ dist/main.js: {}
88
+ dependencies:
89
+ - mytheme/translations
90
+ ```
91
+
92
+ ### Step 2: Attach the library
93
+
94
+ Attach the library in your theme or module:
95
+
96
+ **In your theme's `.info.yml` file:**
97
+ ```yaml
98
+ libraries:
99
+ - mytheme/global
100
+ ```
101
+
102
+ **Or programmatically in a hook:**
103
+ ```php
104
+ /**
105
+ * Implements hook_page_attachments().
106
+ */
107
+ function mytheme_page_attachments(array &$attachments) {
108
+ $attachments['#attached']['library'][] = 'mytheme/global';
109
+ }
110
+ ```
111
+
112
+ ### Step 3: Extract translations
113
+
114
+ Once the library is attached, Drupal's translation system will scan the `translations.js` file during:
115
+
116
+ - **Interface translation updates** (Configuration → Regional and language → User interface translation)
117
+ - **Locale module scanning** when enabled
118
+ - **Drush locale:check** and **locale:update** commands
119
+
120
+ The commented translation calls will be extracted and made available for translation in Drupal's translation interface.
121
+
122
+ ## Features
123
+
124
+ - ✅ Extracts `Drupal.t()` calls
125
+ - ✅ Extracts `Drupal.formatPlural()` calls
126
+ - ✅ Supports single quotes, double quotes, and template literals
127
+ - ✅ Supports multiline strings
128
+ - ✅ Supports strings with parentheses
129
+ - ✅ Supports parameters and context options
130
+ - ✅ Automatically deduplicates identical translations
131
+ - ✅ Sorts output alphabetically
132
+
133
+ ## Options
134
+
135
+ ### `include`
136
+ Type: `string | string[]`
137
+ Default: `undefined`
138
+
139
+ A pattern or array of patterns to include files for extraction.
140
+
141
+ ### `exclude`
142
+ Type: `string | string[]`
143
+ Default: `undefined`
144
+
145
+ A pattern or array of patterns to exclude files from extraction.
@@ -0,0 +1,6 @@
1
+ import type { Plugin } from 'rollup';
2
+ export interface ExtractDrupalTOptions {
3
+ include?: string | string[];
4
+ exclude?: string | string[];
5
+ }
6
+ export default function extractDrupalT(options?: ExtractDrupalTOptions): Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import { createFilter } from '@rollup/pluginutils';
2
+ export default function extractDrupalT(options = {}) {
3
+ const filter = createFilter(options.include, options.exclude);
4
+ const translations = new Set();
5
+ return {
6
+ name: 'extract-drupal-t',
7
+ transform(code, id) {
8
+ if (!filter(id))
9
+ return null;
10
+ // Match Drupal.t('...') and (Drupal).t('...') calls, including parameters.
11
+ // Using [\s\S] instead of . to match multiline strings (including newlines)
12
+ const regex = /((\(Drupal\)|Drupal)\.t\((['"`][\s\S]*?['"`](?:,[\s\S]*?)*?)\))/g;
13
+ let match;
14
+ while ((match = regex.exec(code)) !== null) {
15
+ translations.add(match[0]);
16
+ }
17
+ // Match Drupal.formatPlural('...', '...', '...', ...);
18
+ // Using [\s\S] instead of . to match multiline strings (including newlines)
19
+ const pluralRegex = /((\(Drupal\)|Drupal)\.formatPlural\((\d+),\s*(['"`][\s\S]*?['"`]),\s*(['"`][\s\S]*?['"`]),\s*([\s\S]*?)(?:,\s*([\s\S]*?))?\))/g;
20
+ let pluralMatch;
21
+ while ((pluralMatch = pluralRegex.exec(code)) !== null) {
22
+ translations.add(pluralMatch[0]);
23
+ }
24
+ return null;
25
+ },
26
+ generateBundle() {
27
+ const bundleContent = Array.from(translations)
28
+ // Sort translations to ensure consistent order.
29
+ .sort()
30
+ // Format each translation as a comment.
31
+ // Replace '(Drupal)' with 'Drupal' for consistency.
32
+ // Drupal can find the translations it needs to include.
33
+ .map((translation) => `// ${translation.replace('(Drupal)', 'Drupal')}`)
34
+ .join('\n');
35
+ this.emitFile({
36
+ type: 'asset',
37
+ fileName: 'translations.js',
38
+ source: bundleContent,
39
+ });
40
+ },
41
+ };
42
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "vite-plugin-drupal-t",
3
+ "version": "1.0.0",
4
+ "description": "A Vite plugin that automatically extracts Drupal.t() and Drupal.formatPlural() translation calls for seamless internationalization",
5
+ "author": "Märt Rang <rang501@gmail.com>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/rang501/vite-plugin-drupal-t.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/rang501/vite-plugin-drupal-t/issues"
13
+ },
14
+ "homepage": "https://github.com/rang501/vite-plugin-drupal-t#readme",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "test:ui": "vitest --ui",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "vite",
31
+ "vite-plugin",
32
+ "drupal",
33
+ "translations",
34
+ "i18n",
35
+ "internationalization",
36
+ "l10n",
37
+ "localization",
38
+ "drupal-t",
39
+ "formatPlural"
40
+ ],
41
+ "dependencies": {
42
+ "@rollup/pluginutils": "^4.2.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/estree": "^1.0.7",
46
+ "@types/node": "^24.10.1",
47
+ "@vitest/ui": "^3.2.4",
48
+ "typescript": "^5.0.0",
49
+ "vite": "^7.2.4",
50
+ "vitest": "^3.2.4"
51
+ },
52
+ "peerDependencies": {
53
+ "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
54
+ }
55
+ }