Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

260 lines
11 KiB
TypeScript

import { strings } from '../res/strings';
function isEnglishStrings(s: typeof strings): boolean {
return s.app_name !== strings.app_name;
}
export function getLocalizedWeatherText(text: string | undefined, s: typeof strings): string {
const raw = String(text ?? '').trim();
if (!raw) return '--';
const normalized = raw.toLowerCase();
if (/^[A-Za-z0-9\s\-_/]+$/.test(raw)) {
if (normalized.includes('blizzard') || normalized.includes('storm snow')) return s.weather_storm_snow;
if (normalized.includes('snow')) return normalized.includes('heavy') ? s.weather_heavy_snow : normalized.includes('moderate') ? s.weather_moderate_snow : s.weather_light_snow;
if (normalized.includes('torrential') || normalized.includes('storm rain')) return s.weather_storm_rain;
if (normalized.includes('rain')) return normalized.includes('heavy') ? s.weather_heavy_rain : normalized.includes('moderate') ? s.weather_moderate_rain : s.weather_light_rain;
if (normalized.includes('fog')) return s.weather_fog;
if (normalized.includes('haze')) return normalized.includes('heavy') ? s.weather_heavy_haze : normalized.includes('moderate') ? s.weather_moderate_haze : s.weather_light_haze;
if (normalized.includes('dust')) return s.weather_dust;
if (normalized.includes('sand')) return s.weather_sand;
if (normalized.includes('wind')) return s.weather_wind;
if (normalized.includes('overcast') || normalized.includes('cloudy')) return s.weather_cloudy;
if (normalized.includes('partly')) return s.weather_partly_cloudy;
if (normalized.includes('clear') || normalized.includes('sunny')) return s.weather_clear;
return raw;
}
if (raw.includes('\u96f7') && raw.includes('\u96e8')) return s.weather_storm_rain;
if (raw.includes('\u66b4\u96e8') || raw.includes('\u5927\u96e8')) return s.weather_heavy_rain;
if (raw.includes('\u4e2d\u96e8')) return s.weather_moderate_rain;
if (raw.includes('\u9635\u96e8') || raw.includes('\u5c0f\u96e8') || raw.includes('\u6bdb\u6bdb\u96e8')) return s.weather_light_rain;
if (raw.includes('\u66b4\u96ea') || raw.includes('\u5927\u96ea')) return s.weather_heavy_snow;
if (raw.includes('\u4e2d\u96ea')) return s.weather_moderate_snow;
if (raw.includes('\u9635\u96ea') || raw.includes('\u5c0f\u96ea')) return s.weather_light_snow;
if (raw.includes('\u96fe') || raw.includes('\u6d53\u96fe')) return s.weather_fog;
if (raw.includes('\u91cd\u5ea6\u9727\u973e') || (raw.includes('\u91cd\u5ea6') && raw.includes('\u973e'))) return s.weather_heavy_haze;
if (raw.includes('\u4e2d\u5ea6\u9727\u973e') || (raw.includes('\u4e2d\u5ea6') && raw.includes('\u973e'))) return s.weather_moderate_haze;
if (raw.includes('\u8f7b\u5ea6\u9727\u973e') || raw.includes('\u9727\u973e') || raw.includes('\u973e')) return s.weather_light_haze;
if (raw.includes('\u6d6e\u5c18') || raw.includes('\u626c\u5c18')) return s.weather_dust;
if (raw.includes('\u6c99\u5c18')) return s.weather_sand;
if (raw.includes('\u5927\u98ce')) return s.weather_wind;
if (raw.includes('\u9634')) return s.weather_cloudy;
if (raw.includes('\u591a\u4e91') || raw.includes('\u5c11\u4e91')) return s.weather_partly_cloudy;
if (raw.includes('\u6674')) return s.weather_clear;
return raw;
}
export function getLocalizedLifeIndexName(type: string, name: string, s: typeof strings): string {
switch (type) {
case '1':
return s.index_sport;
case '2':
return s.index_car_wash;
case '3':
return s.index_dressing;
case '5':
return s.index_uv;
case '9':
return s.index_flu;
default:
return name;
}
}
const LIFE_INDEX_CATEGORY_LABELS: Record<string, string> = {
'适宜': 'Suitable',
'较适宜': 'Quite Suitable',
'不宜': 'Not Suitable',
'较不宜': 'Rather Unsuitable',
'极不宜': 'Highly Unsuitable',
'一般': 'Average',
'较好': 'Good',
'良好': 'Good',
'舒适': 'Comfortable',
'较舒适': 'Comfortable',
'较热': 'Warm',
'热': 'Hot',
'炎热': 'Very Hot',
'凉': 'Cool',
'较冷': 'Chilly',
'冷': 'Cold',
'寒冷': 'Very Cold',
'很弱': 'Very Low',
'弱': 'Low',
'中等': 'Moderate',
'强': 'High',
'很强': 'Very High',
'极强': 'Extreme',
'少发': 'Low Risk',
'较少发': 'Slight Risk',
'不易发': 'Unlikely',
'较不易发': 'Low Risk',
'较易发': 'Moderate Risk',
'易发': 'High Risk',
'极易发': 'Very High Risk',
};
const PRECIPITATION_LABELS: Array<{ test: (raw: string) => boolean; value: string }> = [
{
test: (raw) => /未来(?:两|2)小时.*无.*降水|短时内无明显降水|未来\d+小时.*无.*降水|无明显降水/.test(raw),
value: 'No precipitation expected in the next 2 hours',
},
{
test: (raw) => /未来(?:两|2)小时.*(?:小雨|阵雨|毛毛雨)|短时.*(?:小雨|阵雨|毛毛雨)/.test(raw),
value: 'Light rain expected in the next 2 hours',
},
{
test: (raw) => /未来(?:两|2)小时.*中雨|短时.*中雨/.test(raw),
value: 'Moderate rain expected in the next 2 hours',
},
{
test: (raw) => /未来(?:两|2)小时.*(?:大雨|暴雨)|短时.*(?:大雨|暴雨)/.test(raw),
value: 'Heavy rain expected in the next 2 hours',
},
{
test: (raw) => /未来(?:两|2)小时.*雪|短时.*雪/.test(raw),
value: 'Snow expected in the next 2 hours',
},
{
test: (raw) => /未来(?:两|2)小时.*降水|短时.*降水|未来\d+小时.*降水/.test(raw),
value: 'Precipitation expected in the next 2 hours',
},
];
export function getLocalizedLifeIndexCategory(category: string, s: typeof strings, _type?: string): string {
if (!isEnglishStrings(s)) return category;
const raw = String(category ?? '').trim();
if (!raw) return '--';
if (/^[A-Za-z0-9\s/&()-]+$/.test(raw)) return raw;
const mapped = LIFE_INDEX_CATEGORY_LABELS[raw];
if (mapped) return mapped;
const normalized = category.toLowerCase();
if (category.includes('\u4e0d\u5b9c') || normalized.includes('unsuitable')) return s.index_category_unsuitable;
if (category.includes('\u9002\u5b9c') || normalized.includes('suitable')) return s.index_category_suitable;
return category;
}
export function getLocalizedMinutelySummary(summary: string | undefined, s: typeof strings): string {
const raw = String(summary ?? '').trim();
if (!raw) return '';
if (!isEnglishStrings(s)) return raw;
if (/^[A-Za-z0-9\s,./:_()-]+$/.test(raw)) return raw;
const matched = PRECIPITATION_LABELS.find((item) => item.test(raw));
if (matched) return matched.value;
if (raw.includes('降水')) return 'Precipitation expected soon';
return raw;
}
const WIND_DIRECTION_LABELS: Array<{ matches: string[]; value: keyof typeof strings }> = [
{ matches: ['north-northeast', 'north northeast', 'nne', '\u5317\u504f\u4e1c', '\u5317\u4e1c\u5317\u98ce'], value: 'wind_dir_nne' },
{ matches: ['east-northeast', 'east northeast', 'ene', '\u4e1c\u504f\u5317', '\u4e1c\u4e1c\u5317\u98ce'], value: 'wind_dir_ene' },
{ matches: ['east-southeast', 'east southeast', 'ese', '\u4e1c\u504f\u5357', '\u4e1c\u4e1c\u5357\u98ce'], value: 'wind_dir_ese' },
{ matches: ['south-southeast', 'south southeast', 'sse', '\u5357\u504f\u4e1c', '\u5357\u4e1c\u5357\u98ce'], value: 'wind_dir_sse' },
{ matches: ['south-southwest', 'south southwest', 'ssw', '\u5357\u504f\u897f', '\u5357\u897f\u5357\u98ce'], value: 'wind_dir_ssw' },
{ matches: ['west-southwest', 'west southwest', 'wsw', '\u897f\u504f\u5357', '\u897f\u897f\u5357\u98ce'], value: 'wind_dir_wsw' },
{ matches: ['west-northwest', 'west northwest', 'wnw', '\u897f\u504f\u5317', '\u897f\u897f\u5317\u98ce'], value: 'wind_dir_wnw' },
{ matches: ['north-northwest', 'north northwest', 'nnw', '\u5317\u504f\u897f', '\u5317\u897f\u5317\u98ce'], value: 'wind_dir_nnw' },
{ matches: ['northeast', 'north east', 'ne', '\u4e1c\u5317\u98ce'], value: 'wind_dir_ne' },
{ matches: ['southeast', 'south east', 'se', '\u4e1c\u5357\u98ce'], value: 'wind_dir_se' },
{ matches: ['southwest', 'south west', 'sw', '\u897f\u5357\u98ce'], value: 'wind_dir_sw' },
{ matches: ['northwest', 'north west', 'nw', '\u897f\u5317\u98ce'], value: 'wind_dir_nw' },
{ matches: ['north', 'n', '\u5317\u98ce'], value: 'wind_dir_n' },
{ matches: ['east', 'e', '\u4e1c\u98ce'], value: 'wind_dir_e' },
{ matches: ['south', 's', '\u5357\u98ce'], value: 'wind_dir_s' },
{ matches: ['west', 'w', '\u897f\u98ce'], value: 'wind_dir_w' },
];
export function getLocalizedWindDirection(direction: string | undefined, s: typeof strings): string {
const raw = String(direction ?? '').trim();
if (!raw) return '--';
const normalized = raw.toLowerCase();
const matched = WIND_DIRECTION_LABELS.find(item => item.matches.some(match => normalized === match || raw.includes(match)));
if (matched) return s[matched.value];
return raw;
}
const WARNING_TYPE_LABELS: Record<string, { zh: string; en: string }> = {
thunderstorm: { zh: '\u96f7\u96e8', en: 'Thunderstorm' },
rainstorm: { zh: '\u66b4\u96e8', en: 'Rainstorm' },
blizzard: { zh: '\u66b4\u96ea', en: 'Blizzard' },
coldwave: { zh: '\u5bd2\u6f6e', en: 'Cold wave' },
gale: { zh: '\u5927\u98ce', en: 'Gale' },
sandstorm: { zh: '\u6c99\u5c18\u66b4', en: 'Sandstorm' },
hightemperature: { zh: '\u9ad8\u6e29', en: 'High temperature' },
lowtemperature: { zh: '\u4f4e\u6e29', en: 'Low temperature' },
fog: { zh: '\u5927\u96fe', en: 'Fog' },
haze: { zh: '\u973e', en: 'Haze' },
};
const WARNING_LEVEL_LABELS: Record<string, { zh: string; en: string }> = {
blue: { zh: '\u84dd\u8272', en: 'Blue' },
yellow: { zh: '\u9ec4\u8272', en: 'Yellow' },
orange: { zh: '\u6a59\u8272', en: 'Orange' },
red: { zh: '\u7ea2\u8272', en: 'Red' },
};
function findWarningTypeLabel(rawValue: string, english: boolean): string | null {
const normalized = rawValue.trim().toLowerCase();
for (const [key, value] of Object.entries(WARNING_TYPE_LABELS)) {
if (normalized.includes(key) || rawValue.includes(value.zh)) {
return english ? value.en : value.zh;
}
}
return null;
}
function findWarningLevelLabel(rawValue: string, english: boolean): string | null {
const normalized = rawValue.trim().toLowerCase();
for (const [key, value] of Object.entries(WARNING_LEVEL_LABELS)) {
if (normalized.includes(key) || rawValue.includes(value.zh)) {
return english ? value.en : value.zh;
}
}
return null;
}
export function getLocalizedWarningType(
warning: { title?: string; typeName?: string; severity?: string; level?: string },
s: typeof strings,
): string {
const english = isEnglishStrings(s);
const typeLabel =
findWarningTypeLabel(String(warning.typeName ?? ''), english) ??
findWarningTypeLabel(String(warning.title ?? ''), english);
const levelLabel =
findWarningLevelLabel(String(warning.severity ?? ''), english) ??
findWarningLevelLabel(String(warning.level ?? ''), english) ??
findWarningLevelLabel(String(warning.title ?? ''), english);
if (typeLabel && levelLabel) {
return english ? `${typeLabel} ${levelLabel} Warning` : `${typeLabel}${levelLabel}\u9884\u8b66`;
}
if (typeLabel) {
return english ? `${typeLabel} Warning` : `${typeLabel}\u9884\u8b66`;
}
return String(warning.title ?? '').trim() || '--';
}
export function getLocalizedWarningText(
warning: { text?: string; title?: string; typeName?: string; severity?: string; level?: string },
s: typeof strings,
): string {
const raw = String(warning.text ?? '').trim();
if (!raw) return '';
if (!isEnglishStrings(s)) return raw;
if (/^[A-Za-z0-9\s,./:_()-]+$/.test(raw)) return raw;
return `${getLocalizedWarningType(warning, s)} in effect.`;
}