i18n(ar): own the Arabic catalog in the application package; mirror select chevrons

The application package boundary owns src/i18n/locales/ar.json beside the
other catalogs, and the style.css shim test checks that the shim holds only
import statements rather than counting its lines, so adding rtl.css to it
does not read as rules.

Selects pad the chevron's side with padding-inline-end, as do the Radio
filter, the SDR gain select and the voice timeout select, which set their
own padding; rtl.css moves the chevron to the left, where an Arabic field's
text ends. A rail legend's range keeps left-to-right order under its colour
ramp, which always runs low to high from left to right.
This commit is contained in:
Sameh Khamis committed 2026-11-01 20:22:51 -08:00
1 parent bc5d038afd
commit 1f9a453bc4
7 files changed
+64 -9

No files matched your search

+1
View File
@@ -60,6 +60,7 @@
"src/ui/scenePresentation.js",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/locales/ar.json",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
"src/i18n/locales/fr.json",
+5 -4
View File
@@ -112,10 +112,11 @@ test('the DISPLAY rail styles live in their current owner, not the shim', () =>
// these rules lived in the root style.css. That file is an import shim now,
// and putting rules back into it would silently undo the split.
const shim = read('style.css');
assert.ok(
shim.split('\n').length < 40,
'style.css is an import shim, not a stylesheet',
);
const rules = shim
.replace(/\/\*[\s\S]*?\*\//g, '')
.split('\n')
.filter((line) => line.trim() && !/^@import '[^']+';$/.test(line.trim()));
assert.deepEqual(rules, [], 'style.css is an import shim, not a stylesheet');
assert.doesNotMatch(
shim,
/draw-hint|pp-text-input/,
+35
View File
@@ -111,3 +111,38 @@ test('the Space Missions counter reads as a number pair in right-to-left', () =>
assert.match(counters[1], /\[data-mission-index\]/);
assert.match(counters[2], /direction: ltr;/);
});
test('select chevrons and legend ranges follow the field and the ramp in right-to-left', () => {
const field = sheet('form-field.css');
assert.match(
ruleBody(field, 'select.gev-field'),
/padding-inline-end: 22px;/,
);
assert.match(
ruleBody(field, 'select.gev-field--compact'),
/padding-inline-end: 18px;/,
);
// Selects that set their own padding keep the chevron's room at the end.
for (const [name, selector, inline] of [
['radio.css', '#radio-filter', '8px 24px'],
['radio.css', '.sdr-gain-row select', '7px 22px'],
['voice-card.css', '#gev-voice-timeout-select', '0.45rem 22px'],
]) {
const body = ruleBody(sheet(name), selector);
assert.match(body, new RegExp(`padding-inline: ${inline};`));
assert.doesNotMatch(body, /(?:^|\s)padding(?:-right)?:/);
}
const rtl = sheet('rtl.css');
assert.match(
ruleBody(rtl, "html[dir='rtl'] select.gev-field"),
/background-position: left 8px center;/,
);
assert.match(
ruleBody(rtl, "html[dir='rtl'] select.gev-field--compact"),
/background-position: left 6px center;/,
);
assert.match(
ruleBody(rtl, "html[dir='rtl'] .rail-card-scale"),
/direction: ltr;/,
);
});
+2 -2
View File
@@ -19,7 +19,7 @@
select.gev-field {
-webkit-appearance: none;
appearance: none;
padding-right: 22px;
padding-inline-end: 22px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='rgba(255,255,255,0.62)' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
@@ -45,7 +45,7 @@ select.gev-field {
}
select.gev-field--compact {
padding-right: 18px;
padding-inline-end: 18px;
background-position: right 6px center;
}
+4 -2
View File
@@ -49,7 +49,8 @@
#radio-filter {
/* Radio owns its compact density and arrow space; shared fields paint the
palette and disabled state, including this intentional theme exception. */
padding: 7px 24px 7px 8px;
padding-block: 7px;
padding-inline: 8px 24px;
border-radius: 5px;
font: 9px/1.2 var(--font-mono);
--gev-field-border-color: rgba(65, 220, 231, 0.28);
@@ -1128,7 +1129,8 @@ html:not([lang='en']) .data-row-list-text {
.sdr-gain-row select {
flex: 1;
min-width: 0;
padding: 5px 22px 5px 7px;
padding-block: 5px;
padding-inline: 7px 22px;
}
.sdr-adsb-stats {
display: grid;
+15
View File
@@ -205,3 +205,18 @@ html[dir='rtl']
html[dir='rtl'] #radio-tuner-value {
unicode-bidi: plaintext;
}
/* The select chevron sits at the end of the field, where the text stops
(form-field.css pads that side through padding-inline-end). */
html[dir='rtl'] select.gev-field {
background-position: left 8px center;
}
html[dir='rtl'] select.gev-field--compact {
background-position: left 6px center;
}
/* A legend's range is printed under its colour ramp, which always runs low
to high from left to right; the numbers keep that order. */
html[dir='rtl'] .rail-card-scale {
direction: ltr;
}
+2 -1
View File
@@ -553,7 +553,8 @@ body.recording-mode .gev-pointer-reticle {
#gev-voice-timeout-select {
flex: 1 1 auto;
min-width: 0;
padding: 0 22px 0 0.45rem;
padding-block: 0;
padding-inline: 0.45rem 22px;
}
#gev-voice-timeout-input {
flex: 0 0 4.5rem;