mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(i18n): find UI text that is not marked for translation
npm run i18n now also lists, and fails on, markup text and title / aria-label / placeholder without data-i18n*, and string literals that reach the page (textContent, innerHTML, setAttribute, toasts, label:/title: properties and similar) without t(), tn() or msg(). Opt-outs say why where the text lives: translate="no" and data-i18n-runtime in markup, // i18n-ignore in code. src/tools/ is skipped: its text is for AI clients. 534 places are reported today; npm test does not run this part yet. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6ff7afcc8b
commit
ee72133574
3 files changed
+358
-19
No files matched your search
@@ -6,6 +6,8 @@ import {
|
||||
extractMessages,
|
||||
readCatalog,
|
||||
shippedLocales,
|
||||
unmarkedMarkup,
|
||||
unmarkedScript,
|
||||
} from '../../scripts/i18n.mjs';
|
||||
import { LOCALES } from './locale.js';
|
||||
|
||||
@@ -55,3 +57,52 @@ test('checkCatalog reports missing, stale, placeholder and plural problems', ()
|
||||
assert.deepEqual(result.stale, ['Removed']);
|
||||
assert.equal(result.errors.length, 2);
|
||||
});
|
||||
|
||||
test('unmarkedMarkup finds text and attributes without data-i18n*', () => {
|
||||
const texts = (html) => unmarkedMarkup('f.html', html).map((f) => f.text);
|
||||
assert.deepEqual(texts('<span>Close</span>'), ['Close']);
|
||||
assert.deepEqual(texts('<span data-i18n>Close</span>'), []);
|
||||
assert.deepEqual(texts('<button title="Pin it">x</button>'), [
|
||||
'title="Pin it"',
|
||||
]);
|
||||
assert.deepEqual(
|
||||
texts('<button title="Pin it" data-i18n-title>x</button>'),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(
|
||||
texts('<select translate="no"><option>0.9 dB</option></select>'),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(texts('<b data-i18n-runtime><i>OFF</i></b>'), []);
|
||||
assert.deepEqual(
|
||||
texts('<span class="material-symbols-outlined">public</span>'),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(texts('<!-- Note --><span>— 42 —</span>'), []);
|
||||
});
|
||||
|
||||
test('unmarkedScript finds literals that reach the page without t/tn/msg', () => {
|
||||
const texts = (js) => unmarkedScript('f.js', js).map((f) => f.text);
|
||||
assert.deepEqual(texts("el.textContent = 'Saved';"), ['Saved']);
|
||||
assert.deepEqual(texts("el.textContent = t('Saved');"), []);
|
||||
assert.deepEqual(texts("el.textContent = on ? 'On air' : 'Off air';"), [
|
||||
'On air',
|
||||
'Off air',
|
||||
]);
|
||||
assert.deepEqual(texts("el.setAttribute('aria-label', `Open ${name}`);"), [
|
||||
'Open',
|
||||
]);
|
||||
assert.deepEqual(
|
||||
texts("const row = { label: msg('Wind'), id: 'wind' };"),
|
||||
[],
|
||||
);
|
||||
assert.deepEqual(texts("const row = { label: 'Wind' };"), ['Wind']);
|
||||
assert.deepEqual(texts("el.textContent = labelFor('flights');"), []);
|
||||
assert.deepEqual(texts("el.className = 'is-active';"), []);
|
||||
assert.deepEqual(texts("el.title = mode === 'Auto mode' ? a : b;"), []);
|
||||
assert.deepEqual(texts("// el.textContent = 'Old copy';"), []);
|
||||
assert.deepEqual(
|
||||
texts("// i18n-ignore: product name\nel.textContent = 'OpenSky';"),
|
||||
[],
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user