feat(i18n): nine languages, each at its own address; release v6.3.0
The site now speaks German, French, Spanish, Ukrainian, Russian, Chinese, Korean and Hindi alongside English — 623 strings per language, 5,607 translations, covering the landing page, the key exchange, the chat, group calls and every error along the way. Each locale is a real page at a real URL (/de/, /fr/, …), generated at build time from locales/*.json. That is the whole point: the app is client-rendered, so a language that only swaps strings at runtime has no address for a crawler to index and no link anyone can share. Each page carries its own canonical, a reciprocal hreflang cluster and translated schema.org. The URL decides the language, always. A stored preference only applies where the address does not say, and nothing ever redirects on Accept-Language — that is how sites become invisible to search engines outside one country. robots.txt and sitemap.xml did not exist before; both are generated now. Bugs found on the way, each with a test that would have caught it: - partner logos used page-relative paths and 404'd from any /xx/ page - post-build stamped ?v= only into the root shell, leaving locales behind - "Back online" appeared on every page load, not just after being offline - update timestamps were hard-coded to US format for every reader - t() threw on a partial window, taking whole components down with it
This commit is contained in:
+47
-21
@@ -120,30 +120,56 @@ function generateMetaJson() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update versions in index.html
|
||||
* Every generated app shell: the root one plus one per locale subdirectory.
|
||||
*
|
||||
* scripts/build-i18n.js runs before this script and stamps the version it reads from
|
||||
* meta.json — which is still the *previous* build at that point, since meta.json is
|
||||
* regenerated below. The root page used to be the only one re-stamped here, so the
|
||||
* localized pages shipped pointing at an older ?v= than the root did.
|
||||
*/
|
||||
function appShellPaths() {
|
||||
const shells = [path.join(CONFIG.publicDir, 'index.html')];
|
||||
try {
|
||||
const sitePath = path.join(CONFIG.publicDir, 'locales', 'site.json');
|
||||
if (fs.existsSync(sitePath)) {
|
||||
const site = JSON.parse(fs.readFileSync(sitePath, 'utf-8'));
|
||||
for (const code of site.locales || []) {
|
||||
if (code === site.defaultLocale) continue;
|
||||
shells.push(path.join(CONFIG.publicDir, code, 'index.html'));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not read locales/site.json:', error.message);
|
||||
}
|
||||
return shells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update versions in every index.html
|
||||
*/
|
||||
function updateIndexHtmlVersions(buildVersion) {
|
||||
try {
|
||||
const indexHtmlPath = path.join(CONFIG.publicDir, 'index.html');
|
||||
|
||||
if (!fs.existsSync(indexHtmlPath)) {
|
||||
console.warn('⚠️ index.html not found, skipping version update');
|
||||
return;
|
||||
}
|
||||
|
||||
let indexHtml = fs.readFileSync(indexHtmlPath, 'utf-8');
|
||||
|
||||
// Update versions in query parameters for JS files
|
||||
// Pattern: src="dist/app.js?v=..." or src="dist/app-boot.js?v=..."
|
||||
// Also replace BUILD_VERSION placeholder
|
||||
indexHtml = indexHtml.replace(/\?v=BUILD_VERSION/g, `?v=${buildVersion}`);
|
||||
indexHtml = indexHtml.replace(/\?v=(\d+)/g, `?v=${buildVersion}`);
|
||||
|
||||
fs.writeFileSync(indexHtmlPath, indexHtml, 'utf-8');
|
||||
console.log('✅ index.html versions updated');
|
||||
for (const indexHtmlPath of appShellPaths()) {
|
||||
const label = path.relative(CONFIG.publicDir, indexHtmlPath);
|
||||
try {
|
||||
if (!fs.existsSync(indexHtmlPath)) {
|
||||
console.warn(`⚠️ ${label} not found, skipping version update`);
|
||||
continue;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Failed to update index.html versions:', error.message);
|
||||
let indexHtml = fs.readFileSync(indexHtmlPath, 'utf-8');
|
||||
|
||||
// Update versions in query parameters for JS files
|
||||
// Pattern: src="dist/app.js?v=..." or src="dist/app-boot.js?v=..."
|
||||
// Also replace BUILD_VERSION placeholder
|
||||
indexHtml = indexHtml.replace(/\?v=BUILD_VERSION/g, `?v=${buildVersion}`);
|
||||
indexHtml = indexHtml.replace(/\?v=(\d+)/g, `?v=${buildVersion}`);
|
||||
|
||||
fs.writeFileSync(indexHtmlPath, indexHtml, 'utf-8');
|
||||
console.log(`✅ ${label} versions updated`);
|
||||
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Failed to update ${label} versions:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user