From ec48d3793234847e45fe56984edc7c91f2f1fc7b Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 26 Aug 2023 22:02:33 +0200 Subject: [PATCH] Implemented i18n cache busting --- .../translate-browser.loader.ts | 3 +- .../translate-server.loader.ts | 3 +- webpack/helpers.ts | 47 +++++++++++++++---- webpack/webpack.common.ts | 10 ++-- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/ngx-translate-loaders/translate-browser.loader.ts b/src/ngx-translate-loaders/translate-browser.loader.ts index a6188c9f15c..0379d87c1a6 100644 --- a/src/ngx-translate-loaders/translate-browser.loader.ts +++ b/src/ngx-translate-loaders/translate-browser.loader.ts @@ -35,7 +35,8 @@ export class TranslateBrowserLoader implements TranslateLoader { } else { // If they're not available on the transfer state (e.g. when running in dev mode), retrieve // them using HttpClient - return this.http.get('' + this.prefix + lang + this.suffix, { responseType: 'text' }).pipe( + const translationHash: string = (process.env.languageHashes as any)[lang + '.json5']; + return this.http.get(`${this.prefix}${lang}.${translationHash}${this.suffix}`, { responseType: 'text' }).pipe( map((json: any) => JSON.parse(json)) ); } diff --git a/src/ngx-translate-loaders/translate-server.loader.ts b/src/ngx-translate-loaders/translate-server.loader.ts index c09c71f0499..1f47dfe95b5 100644 --- a/src/ngx-translate-loaders/translate-server.loader.ts +++ b/src/ngx-translate-loaders/translate-server.loader.ts @@ -23,8 +23,9 @@ export class TranslateServerLoader implements TranslateLoader { * @param lang the language code */ public getTranslation(lang: string): Observable { + const translationHash: string = (process.env.languageHashes as any)[lang + '.json5']; // Retrieve the file for the given language, and parse it - const messages = JSON.parse(readFileSync(`${this.prefix}${lang}${this.suffix}`, 'utf8')); + const messages = JSON.parse(readFileSync(`${this.prefix}${lang}.${translationHash}${this.suffix}`, 'utf8')); // Store the parsed messages in the transfer state so they'll be available immediately when the // app loads on the client this.storeInTransferState(lang, messages); diff --git a/webpack/helpers.ts b/webpack/helpers.ts index 43855f6c729..f0b42a8a690 100644 --- a/webpack/helpers.ts +++ b/webpack/helpers.ts @@ -1,18 +1,49 @@ -const path = require('path'); +import { readFileSync, readdirSync, statSync, Stats } from 'fs'; +import { join, resolve } from 'path'; + +const md5 = require('md5'); export const projectRoot = (relativePath) => { - return path.resolve(__dirname, '..', relativePath); + return resolve(__dirname, '..', relativePath); }; export const globalCSSImports = () => { return [ - projectRoot(path.join('src', 'styles', '_variables.scss')), - projectRoot(path.join('src', 'styles', '_mixins.scss')), + projectRoot(join('src', 'styles', '_variables.scss')), + projectRoot(join('src', 'styles', '_mixins.scss')), ]; }; +/** + * Calculates the md5 hash of a file + * + * @param filePath The path of the file + */ +export function calculateFileHash(filePath: string): string { + const fileContent: Buffer = readFileSync(filePath); + return md5(fileContent); +} -module.exports = { - projectRoot, - globalCSSImports -}; +/** + * Calculate the hashes of all the files (matching the given regex) in a certain folder + * + * @param folderPath The path of the folder + * @param regExp A regex of the files in the folder for which a hash needs to be generated + */ +export function getFileHashes(folderPath: string, regExp: RegExp): { [fileName: string]: string } { + const files: string[] = readdirSync(folderPath); + let hashes: { [fileName: string]: string } = {}; + + for (const file of files) { + if (file.match(regExp)) { + const filePath: string = join(folderPath, file); + const stats: Stats = statSync(filePath); + + if (stats.isFile()) { + hashes[file] = calculateFileHash(filePath); + } + } + } + + return hashes; +} diff --git a/webpack/webpack.common.ts b/webpack/webpack.common.ts index 1a1ecfd6efe..75e514acae1 100644 --- a/webpack/webpack.common.ts +++ b/webpack/webpack.common.ts @@ -1,4 +1,5 @@ -import { globalCSSImports, projectRoot } from './helpers'; +import { globalCSSImports, projectRoot, getFileHashes, calculateFileHash } from './helpers'; +import { EnvironmentPlugin } from 'webpack'; const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); @@ -19,11 +20,11 @@ export const copyWebpackOptions = { const matches = absoluteFilename.match(/.*[\/|\\]assets[\/|\\](.+)\.json5$/); if (matches) { // matches[1] is the relative path from src/assets to the JSON5 file, without the extension - return path.join('assets', matches[1] + '.json'); + return path.join('assets', `${matches[1]}.${calculateFileHash(absoluteFilename)}.json`); } }, transform(content) { - return JSON.stringify(JSON5.parse(content.toString())) + return JSON.stringify(JSON5.parse(content.toString())); } }, { @@ -77,6 +78,9 @@ const SCSS_LOADERS = [ export const commonExports = { plugins: [ + new EnvironmentPlugin({ + languageHashes: getFileHashes(path.join(__dirname, '..', 'src', 'assets', 'i18n'), /.*\.json5/g), + }), new CopyWebpackPlugin(copyWebpackOptions), ], module: {