diff --git a/README.md b/README.md index b64547d..60bc180 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Privacy-first audio tools that run entirely in your browser. No uploads, no acco | **Audio Joiner** | [/audio-joiner/](https://audiotools.dev/audio-joiner/) | Web Audio API, crossfade | | **Audio Compressor** | [/audio-compressor/](https://audiotools.dev/audio-compressor/) | Web Audio API + Mediabunny MP3 | -Chinese pages: [/zh/](https://audiotools.dev/zh/) and [/zh/converter/](https://audiotools.dev/zh/converter/). +Chinese pages: [/zh/](https://audiotools.dev/zh/), [/zh/converter/](https://audiotools.dev/zh/converter/), and /zh/ versions of Volume Booster, Text to Speech, Audio Joiner and Audio Compressor. ## Architecture @@ -57,6 +57,7 @@ audiotools.dev/ - `python3 scripts/seo_sync.py` rewrites the FAQPage JSON-LD on every page from the FAQ that is visible on that page, and refreshes `` in `sitemap.xml` from git. Run it after editing a FAQ or a page (needs `beautifulsoup4`). - CI (`.github/workflows/seo-check.yml`) runs `scripts/seo_sync.py --check` and fails if the markup and the visible FAQ drift apart. +- `python3 scripts/build_zh.py` rebuilds the Chinese tool pages (volume booster, text to speech, joiner, compressor) from the English ones with the translation tables in the script, and keeps hreflang, the 中文/EN switch and the Chinese navigation in step. It stops if an English string it translates has changed. Run `scripts/seo_sync.py` after it. - Social preview images live in `og/` (1200×630, one per tool). ## Deployment diff --git a/assistant.js b/assistant.js index afad3d4..3c80942 100644 --- a/assistant.js +++ b/assistant.js @@ -137,6 +137,7 @@ const takePending = () => store('readwrite', (s) => { const g = s.get('pending'); s.delete('pending'); return g; }); // ---------- tool pages: "continue with this file" after a result ---------- + const ZH_PAGES = ['converter', 'volume-booster', 'text-to-speech', 'audio-joiner', 'audio-compressor']; // tools with a Chinese page const NEXT = [['audio-compressor', 'Compress', '压缩'], ['audio-trimmer', 'Trim', '剪辑'], ['volume-booster', 'Boost volume', '增强音量'], ['converter', 'Convert format', '转换格式'], ['audio-speed-changer', 'Change speed', '变速'], ['audio-joiner', 'Join with more', '合并']]; window.offerNext = function (tool, anchor, file) { @@ -148,7 +149,7 @@ el.append(T('Continue with this file:', '继续处理此文件:')); NEXT.filter(([t]) => t !== tool).slice(0, 3).forEach(([t, en, zh]) => { const a = document.createElement('a'); - a.href = t === 'converter' && ZH ? '/zh/converter/' : '/' + t + '/'; + a.href = (ZH && ZH_PAGES.includes(t) ? '/zh/' : '/') + t + '/'; a.textContent = T(en, zh); a.addEventListener('click', async (e) => { e.preventDefault(); @@ -185,16 +186,16 @@ 'converter': { url: ZH ? '/zh/converter/' : '/converter/', name: T('Audio Converter', '音频转换器'), kw: ['convert', 'format', 'mp3', 'wav', 'flac', 'ogg', 'm4a', 'aac', 'webm', 'mp4', 'video', 'extract audio', 'конверт', 'формат', 'преобраз', 'видео', '转换', '格式', '视频', '提取音频'] }, - 'audio-compressor': { url: '/audio-compressor/', name: T('Audio Compressor', '音频压缩'), + 'audio-compressor': { url: ZH ? '/zh/audio-compressor/' : '/audio-compressor/', name: T('Audio Compressor', '音频压缩'), kw: ['compress', 'smaller', 'reduce', 'file size', 'shrink', 'bitrate', 'email', 'telegram', 'whatsapp', 'сжать', 'уменьш', 'размер', 'меньше', 'битрейт', '压缩', '变小', '缩小', '大小', '体积'] }, - 'volume-booster': { url: '/volume-booster/', name: T('Volume Booster', '音量增强'), + 'volume-booster': { url: ZH ? '/zh/volume-booster/' : '/volume-booster/', name: T('Volume Booster', '音量增强'), kw: ['quiet', 'louder', 'volume', 'boost', 'normalize', 'amplify', 'gain', 'тих', 'громк', 'громч', 'усил', 'нормализ', '音量', '声音太小', '太小声', '放大', '增强', '标准化'] }, 'audio-trimmer': { url: '/audio-trimmer/', name: T('Audio Trimmer', '音频剪辑'), kw: ['trim', 'cut', 'crop', 'shorten', 'ringtone', 'clip', 'remove the start', 'remove the end', 'обрез', 'обреж', 'вырез', 'отрез', 'отреж', 'рингтон', 'укорот', '剪辑', '裁剪', '剪切', '铃声', '截取'] }, - 'audio-joiner': { url: '/audio-joiner/', name: T('Audio Joiner', '音频合并'), + 'audio-joiner': { url: ZH ? '/zh/audio-joiner/' : '/audio-joiner/', name: T('Audio Joiner', '音频合并'), kw: ['join', 'merge', 'combine', 'concatenate', 'several files', 'multiple files', 'склеить', 'склей', 'объедин', 'соедин', 'несколько', '合并', '拼接', '连接', '多个'] }, 'audio-speed-changer': { url: '/audio-speed-changer/', name: T('Speed Changer', '音频变速'), @@ -203,7 +204,7 @@ 'voice-to-text': { url: '/voice-to-text/', name: T('Voice to Text', '语音转文字'), kw: ['transcri', 'to text', 'subtitle', 'speech to text', 'dictat', 'voice message', 'lecture', 'interview', 'расшифр', 'в текст', 'субтитр', 'голосов', 'лекци', 'диктов', '转文字', '转录', '字幕', '语音转', '听写'] }, - 'text-to-speech': { url: '/text-to-speech/', name: T('Text to Speech', '文字转语音'), + 'text-to-speech': { url: ZH ? '/zh/text-to-speech/' : '/text-to-speech/', name: T('Text to Speech', '文字转语音'), kw: ['text to speech', 'read aloud', 'tts', 'voice over', 'narrat', 'speak', 'озвуч', 'вслух', 'синтез речи', 'прочитать', '朗读', '配音', '文字转语音', '语音合成'] }, }; diff --git a/audio-compressor/index.html b/audio-compressor/index.html index da9b650..29aed3c 100644 --- a/audio-compressor/index.html +++ b/audio-compressor/index.html @@ -17,6 +17,9 @@ + + + @@ -142,6 +145,7 @@ Text to Speech Voice to Text All tools + 中文 @@ -225,7 +229,7 @@

Audio Compressor

Free Online Audio Compressor

Audio Compressor reduces the file size of your audio by re-encoding it to MP3 at a bitrate and sample rate you choose. Perfect for fitting audio into email attachments, messaging apps, or storage limits.

How It Works

-

Lossy MP3 compression removes inaudible data to shrink the file. A 44 MB WAV can become a 4 MB MP3 at 128 kbps — roughly a 10x reduction with minimal quality loss for most uses. Higher bitrates (256–320 kbps) preserve more detail for music. Everything runs in your browser via the Web Audio API and a JavaScript MP3 encoder — nothing is uploaded.

+

Lossy MP3 compression removes inaudible data to shrink the file. A 44 MB WAV can become a 4 MB MP3 at 128 kbps — roughly a 10x reduction with minimal quality loss for most uses. Higher bitrates (256–320 kbps) preserve more detail for music. Everything runs in your browser, with the LAME MP3 encoder compiled to WebAssembly — nothing is uploaded.

Choosing the Right Bitrate

64 kbps — voice recordings and podcasts (smallest files). 128 kbps — general purpose, good quality for most uses. 192 kbps — high quality for music. 320 kbps — maximum quality, indistinguishable from uncompressed for most listeners. Lowering the sample rate to 22 kHz or 16 kHz shrinks speech files further.

Need other edits? Try our Audio Converter, Audio Trimmer, Audio Joiner, or Volume Booster.

diff --git a/audio-export.js b/audio-export.js index 15fc49e..d6a5776 100644 --- a/audio-export.js +++ b/audio-export.js @@ -5,7 +5,7 @@ window.downloadAudio = async function (buffer, wavBlob, select, baseName, button) { const mp3 = select.value === 'mp3'; const label = button && button.textContent; - if (button) { button.disabled = true; if (mp3) button.textContent = 'Encoding MP3…'; } + if (button) { button.disabled = true; if (mp3) button.textContent = document.documentElement.lang.startsWith('zh') ? '正在编码 MP3…' : 'Encoding MP3…'; } try { const blob = mp3 ? await (await import('/media.js')).encodeBuffer(buffer, { format: 'mp3', bitrate: 192000 }) : wavBlob; const a = document.createElement('a'); diff --git a/audio-joiner/index.html b/audio-joiner/index.html index 36cee01..aa16135 100644 --- a/audio-joiner/index.html +++ b/audio-joiner/index.html @@ -17,6 +17,9 @@ + + + @@ -137,6 +140,7 @@ Text to Speech Voice to Text All tools + 中文 diff --git a/changelog/index.html b/changelog/index.html index 631f925..c050bc8 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -75,6 +75,8 @@

Changelog