mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Merge branch 'main' into fix/remove-operator-hud-layout
This commit is contained in:
22 files changed
+3262
-35
No files matched your search
@@ -1,6 +1,6 @@
|
||||
/** Contact readouts and nearby-signal presentation for the Cockpit controller. */
|
||||
import * as Cesium from 'cesium';
|
||||
import { formatNumber, getLocale, t, tn } from '../i18n/index.js';
|
||||
import { formatNumber, t, tn } from '../i18n/index.js';
|
||||
import { unitLabel } from './unitLabels.js';
|
||||
import {
|
||||
bearingBetweenCoordinates,
|
||||
@@ -8,14 +8,17 @@ import {
|
||||
relativeBearing,
|
||||
resolveCockpitContextReadout,
|
||||
} from '../cockpitMath.js';
|
||||
import { formatCockpitDistance } from './cockpitPresentation.js';
|
||||
import {
|
||||
cockpitUnitCase,
|
||||
formatCockpitDistance,
|
||||
} from './cockpitPresentation.js';
|
||||
|
||||
/** The Context scope line in the active language (see cockpitMath.js). */
|
||||
const LOCALIZED_CONTEXT_SCOPE = Object.freeze({
|
||||
window: ({ label, radiusKm }) =>
|
||||
t('{label} · {radius} AIR/SEA WINDOW', {
|
||||
label,
|
||||
radius: `${radiusKm === null ? '—' : formatNumber(radiusKm)}\u00a0${getLocale().startsWith('zh-') ? unitLabel('km') : unitLabel('km').toUpperCase()}`,
|
||||
radius: `${radiusKm === null ? '—' : formatNumber(radiusKm)}\u00a0${cockpitUnitCase(unitLabel('km'))}`,
|
||||
}),
|
||||
installations: ({ scope, coverage }) =>
|
||||
t('{scope} · INSTALLATIONS {coverage}', { scope, coverage }),
|
||||
|
||||
@@ -149,7 +149,15 @@ export function formatCockpitDistance(distanceM) {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
});
|
||||
return getLocale().startsWith('zh-') ? distance : distance.toUpperCase();
|
||||
return cockpitUnitCase(distance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upper-case a cockpit unit the HUD way (`9.5 KM`), except in Chinese and
|
||||
* Japanese, which have no case and keep units as the catalog writes them.
|
||||
*/
|
||||
export function cockpitUnitCase(text) {
|
||||
return /^(zh|ja)(-|$)/.test(getLocale()) ? text : text.toUpperCase();
|
||||
}
|
||||
|
||||
export function formatCockpitWindDirection(value) {
|
||||
|
||||
@@ -43,7 +43,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['ru', 'uk', 'de', 'en', 'es', 'fr', 'it', 'pt', 'zh-CN', 'zh-Hant'],
|
||||
['ru', 'uk', 'de', 'en', 'es', 'fr', 'it', 'pt', 'zh-CN', 'zh-Hant', 'ja'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
codes(
|
||||
@@ -53,7 +53,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['es', 'de', 'en', 'fr', 'it', 'pt', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
['es', 'de', 'en', 'fr', 'it', 'pt', 'ru', 'uk', 'zh-CN', 'zh-Hant', 'ja'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
codes(
|
||||
@@ -63,7 +63,7 @@ test('the current language comes first, the rest by their own names', () => {
|
||||
nativeNames: LOCALE_NAMES,
|
||||
}),
|
||||
),
|
||||
['en', 'de', 'es', 'fr', 'it', 'pt', 'ru', 'uk', 'zh-CN', 'zh-Hant'],
|
||||
['en', 'de', 'es', 'fr', 'it', 'pt', 'ru', 'uk', 'zh-CN', 'zh-Hant', 'ja'],
|
||||
);
|
||||
const rows = buildLocaleOptions({
|
||||
locales: MANY,
|
||||
|
||||
+44
-16
@@ -1,6 +1,7 @@
|
||||
/* Tight tracking keeps Chinese labels readable; preserve Latin tracking for
|
||||
intel HUD readouts and content marked translate="no", including its containers. */
|
||||
html:lang(zh)
|
||||
/* Chinese and Japanese text. Tight tracking keeps CJK labels readable;
|
||||
preserve Latin tracking for intel HUD readouts and content marked
|
||||
translate="no", including its containers. */
|
||||
html:is(:lang(zh), :lang(ja))
|
||||
body
|
||||
:not(
|
||||
#intel-hud,
|
||||
@@ -12,40 +13,67 @@ html:lang(zh)
|
||||
letter-spacing: 0.02em !important;
|
||||
}
|
||||
|
||||
/* Keep small Chinese labels legible at a minimum of 9px. */
|
||||
html:lang(zh) #command-dock .gev-mic-label,
|
||||
html:lang(zh) #command-dock #gev-voice-detail,
|
||||
html:lang(zh) .gev-voice-tier-btn,
|
||||
html:lang(zh) .panel-layer-toggle {
|
||||
/* Japanese line breaking: never start a line with ー, a small kana or closing
|
||||
punctuation (プレビュ / ー). */
|
||||
html:lang(ja) body {
|
||||
line-break: strict;
|
||||
}
|
||||
|
||||
/* Keep small CJK labels legible at a minimum of 9px. */
|
||||
html:is(:lang(zh), :lang(ja)) #command-dock .gev-mic-label,
|
||||
html:is(:lang(zh), :lang(ja)) #command-dock #gev-voice-detail,
|
||||
html:is(:lang(zh), :lang(ja)) .gev-voice-tier-btn,
|
||||
html:is(:lang(zh), :lang(ja)) .panel-layer-toggle {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* A two-glyph button label must not break between its glyphs. */
|
||||
html:lang(zh) .pp-mode-btn {
|
||||
html:is(:lang(zh), :lang(ja)) .pp-mode-btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Japanese segment labels are short words (すべて, 平面): keep each whole. */
|
||||
html:lang(ja) .gev-segment {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Keep unit symbols in their catalog case, including the cockpit readouts. */
|
||||
html:lang(zh) #intel-hud *,
|
||||
html:lang(zh) #cockpit-hud * {
|
||||
html:is(:lang(zh), :lang(ja)) #intel-hud *,
|
||||
html:is(:lang(zh), :lang(ja)) #cockpit-hud * {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/* Move short travel modes (步行 / 骑行) to the next line as whole words. */
|
||||
html:lang(zh) [data-layer-id='directions'] .data-toggle-meta {
|
||||
/* Move short travel modes (步行 / 骑行, 徒歩 / 自転車) to the next line as
|
||||
whole words. */
|
||||
html:is(:lang(zh), :lang(ja)) [data-layer-id='directions'] .data-toggle-meta {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Leave room for the complete sensor name and resolution on two lines. */
|
||||
html:lang(zh) .recent-imagery-readout .ri-strip {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-strip {
|
||||
height: 131px;
|
||||
}
|
||||
html:lang(zh) .recent-imagery-readout .ri-card {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-card {
|
||||
height: 123px;
|
||||
grid-template-rows: 42px 13px 22px 11px 18px;
|
||||
}
|
||||
html:lang(zh) .recent-imagery-readout .ri-card-sensor {
|
||||
html:is(:lang(zh), :lang(ja)) .recent-imagery-readout .ri-card-sensor {
|
||||
white-space: normal;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
/* The Japanese imagery slot line wraps at its · separators, never inside a
|
||||
word (プレ / ビュー); a run too long for the line still breaks. */
|
||||
html:lang(ja) .recent-imagery-readout .ri-slot-value {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Japanese layer status lines wrap at their spaces and · separators, so an
|
||||
age such as 1分以内 stays whole (更新 1 / 分以内); a run too long for the
|
||||
line still breaks. */
|
||||
html:lang(ja) .data-toggle-meta {
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
+13
-2
@@ -1,4 +1,4 @@
|
||||
import { formatNumber, msg, t } from '../i18n/index.js';
|
||||
import { formatNumber, getLocale, msg, t } from '../i18n/index.js';
|
||||
|
||||
// Unit ids stay English in data, params and AI-tool readings; only the label
|
||||
// shown on the page is translated. dBZ and °C read the same in every language.
|
||||
@@ -23,6 +23,17 @@ export function unitLabel(unit) {
|
||||
return Object.hasOwn(UNIT_LABELS, unit) ? t(UNIT_LABELS[unit]) : unit || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* What goes between a number and its unit label: a no-break space, except
|
||||
* before a Japanese unit word, which follows the number directly (5分, 1時間).
|
||||
*/
|
||||
export function quantitySeparator(label) {
|
||||
return getLocale() === 'ja' &&
|
||||
/^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(label)
|
||||
? ''
|
||||
: '\u00a0';
|
||||
}
|
||||
|
||||
/**
|
||||
* A number and its unit that never break apart: "22,0 км/ч". Readings carry
|
||||
* no digit grouping ("1008 hPa"), as on weather charts.
|
||||
@@ -30,7 +41,7 @@ export function unitLabel(unit) {
|
||||
export function formatQuantity(value, unit, options) {
|
||||
const label = unitLabel(unit);
|
||||
const number = formatNumber(value, { useGrouping: false, ...options });
|
||||
return label ? `${number}\u00a0${label}` : number;
|
||||
return label ? `${number}${quantitySeparator(label)}${label}` : number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,3 +44,14 @@ test('other languages get their decimal separator and no digit grouping', () =>
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
|
||||
test('Japanese unit words follow the number directly; symbols keep the space', () => {
|
||||
try {
|
||||
setLocale('ja');
|
||||
assert.equal(formatQuantity(5, 'min'), '5分');
|
||||
assert.equal(formatQuantity(1, 'h'), '1時間');
|
||||
assert.equal(formatQuantity(95, 'kt'), '95\u00a0kt');
|
||||
} finally {
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user