mirror of
https://github.com/jamescliffordspratt/macros.git
synced 2026-07-22 05:46:53 +00:00
Add zh-CN translation, update I18nManager, and refine formatting for localized UI and serving units
This commit is contained in:
parent
0cc2abe444
commit
9ae2844453
7 changed files with 1318 additions and 1064 deletions
|
|
@ -211,39 +211,44 @@ export class I18nManager {
|
|||
*/
|
||||
private detectUserLocale(): string {
|
||||
try {
|
||||
// Get locale from Obsidian's native settings
|
||||
// Get locale from Obsidian's native settings using safe methods
|
||||
const obsidianLocale =
|
||||
this.getMomentLocale() || // Use existing safe method
|
||||
document.documentElement.lang ||
|
||||
(this.app as any).vault?.config?.userInterfaceMode ||
|
||||
this.getMomentLocale() ||
|
||||
(this.app as any).locale;
|
||||
|
||||
if (obsidianLocale && this.isLocaleSupported(obsidianLocale)) {
|
||||
this.plugin.logger.debug(`Detected Obsidian locale: ${obsidianLocale}`);
|
||||
return obsidianLocale;
|
||||
}
|
||||
this.plugin.logger.debug(`Detected Obsidian locale: ${obsidianLocale}`);
|
||||
|
||||
// If Obsidian locale isn't supported, try language code only
|
||||
if (obsidianLocale) {
|
||||
const languageCode = obsidianLocale.split('-')[0];
|
||||
// Handle Chinese locale variants - map both to zh-CN
|
||||
if (obsidianLocale.toLowerCase().startsWith('zh')) {
|
||||
this.plugin.logger.debug(`Mapping Chinese variant ${obsidianLocale} to zh-CN`);
|
||||
return 'zh-CN';
|
||||
}
|
||||
|
||||
// Check if exactly supported
|
||||
if (this.isLocaleSupported(obsidianLocale)) {
|
||||
return obsidianLocale;
|
||||
}
|
||||
|
||||
// Try language code only (e.g., en-GB -> en)
|
||||
const languageCode = obsidianLocale.split('-')[0].toLowerCase();
|
||||
if (this.isLocaleSupported(languageCode)) {
|
||||
this.plugin.logger.debug(`Using language code from Obsidian locale: ${languageCode}`);
|
||||
this.plugin.logger.debug(`Using language code: ${languageCode}`);
|
||||
return languageCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to browser locale as last resort
|
||||
// Fallback to browser locale
|
||||
const browserLocale = navigator.language || navigator.languages?.[0];
|
||||
if (browserLocale) {
|
||||
// Try exact match first
|
||||
if (this.isLocaleSupported(browserLocale)) {
|
||||
this.plugin.logger.debug(`Using browser locale: ${browserLocale}`);
|
||||
return browserLocale;
|
||||
}
|
||||
|
||||
// Try language code only (e.g., 'en' from 'en-US')
|
||||
const languageCode = browserLocale.split('-')[0];
|
||||
if (this.isLocaleSupported(languageCode)) {
|
||||
this.plugin.logger.debug(`Using language code from browser: ${languageCode}`);
|
||||
return languageCode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import MacrosPlugin from './../../main';
|
||||
import MacrosPlugin from '../main';
|
||||
import { Chart, ChartConfiguration, ChartDataset, TooltipItem, registerables } from 'chart.js';
|
||||
import { ChartLoader, MacrosState, DOMUtils } from '../utils';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
ProgressBarFactory,
|
||||
} from '../../../utils';
|
||||
import { parseGrams } from '../../../utils/parsingUtils';
|
||||
import { formatServing } from '../../../utils/formatters';
|
||||
import { Notice, Modal, ButtonComponent } from 'obsidian';
|
||||
import { t } from '../../../lang/I18nManager';
|
||||
|
||||
|
|
@ -585,7 +586,7 @@ export class RowRenderer {
|
|||
const quantityCell = r.insertCell();
|
||||
quantityCell.classList.add('editable-quantity');
|
||||
safeAttachTooltip(quantityCell, t('table.actions.clickToEdit'), this.plugin);
|
||||
quantityCell.textContent = row.serving;
|
||||
quantityCell.textContent = formatServing(row.serving);
|
||||
|
||||
const servingValue = parseGrams(row.serving);
|
||||
|
||||
|
|
|
|||
|
|
@ -179,8 +179,9 @@ export class MacrosDashboard {
|
|||
text: value,
|
||||
});
|
||||
|
||||
// Create custom tooltip with proper energy unit handling
|
||||
// Create custom tooltip with proper energy unit handling and Chinese word order
|
||||
let tooltipMessage: string;
|
||||
const currentLocale = this.plugin.i18nManager.getCurrentLocale();
|
||||
|
||||
if (label === t('table.headers.calories')) {
|
||||
const currentUnit = this.plugin.settings.energyUnit;
|
||||
|
|
@ -193,12 +194,20 @@ export class MacrosDashboard {
|
|||
const remainingKj = targetKj - consumedKj;
|
||||
const percentage = targetKj > 0 ? (consumedKj / targetKj) * 100 : 0;
|
||||
|
||||
tooltipMessage = `${consumedKj.toFixed(1)} kJ • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
|
||||
if (remainingKj > 0) {
|
||||
tooltipMessage += ` • ${remainingKj.toFixed(1)} kJ ${t('general.remaining')}`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipMessage += ` • ${Math.abs(remainingKj).toFixed(1)} kJ ${t('table.summary.over')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
tooltipMessage = `${consumedKj.toFixed(1)} kJ • 占 ${Math.round(percentage)}%`;
|
||||
if (remainingKj > 0) {
|
||||
tooltipMessage += ` • 剩余 ${remainingKj.toFixed(1)} kJ`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipMessage += ` • 超出 ${Math.abs(remainingKj).toFixed(1)} kJ`;
|
||||
}
|
||||
} else {
|
||||
tooltipMessage = `${consumedKj.toFixed(1)} kJ • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
if (remainingKj > 0) {
|
||||
tooltipMessage += ` • ${remainingKj.toFixed(1)} kJ ${t('general.remaining')}`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipMessage += ` • ${Math.abs(remainingKj).toFixed(1)} kJ ${t('table.summary.over')}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use original kcal values for kcal display
|
||||
|
|
@ -207,18 +216,39 @@ export class MacrosDashboard {
|
|||
const remainingKcal = targetKcal - consumedKcal;
|
||||
const percentage = targetKcal > 0 ? (consumedKcal / targetKcal) * 100 : 0;
|
||||
|
||||
tooltipMessage = `${consumedKcal.toFixed(1)} kcal • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
|
||||
if (remainingKcal > 0) {
|
||||
tooltipMessage += ` • ${remainingKcal.toFixed(1)} kcal ${t('general.remaining')}`;
|
||||
} else if (remainingKcal < 0) {
|
||||
tooltipMessage += ` • ${Math.abs(remainingKcal).toFixed(1)} kcal ${t('table.summary.over')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
tooltipMessage = `${consumedKcal.toFixed(1)} kcal • 占 ${Math.round(percentage)}%`;
|
||||
if (remainingKcal > 0) {
|
||||
tooltipMessage += ` • 剩余 ${remainingKcal.toFixed(1)} kcal`;
|
||||
} else if (remainingKcal < 0) {
|
||||
tooltipMessage += ` • 超出 ${Math.abs(remainingKcal).toFixed(1)} kcal`;
|
||||
}
|
||||
} else {
|
||||
tooltipMessage = `${consumedKcal.toFixed(1)} kcal • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
if (remainingKcal > 0) {
|
||||
tooltipMessage += ` • ${remainingKcal.toFixed(1)} kcal ${t('general.remaining')}`;
|
||||
} else if (remainingKcal < 0) {
|
||||
tooltipMessage += ` • ${Math.abs(remainingKcal).toFixed(1)} kcal ${t('table.summary.over')}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use the standard formatDashboardTooltip for non-calorie metrics
|
||||
const numericValue = parseFloat(value.replace('g', ''));
|
||||
tooltipMessage = formatDashboardTooltip(numericValue, target, label);
|
||||
|
||||
if (currentLocale === 'zh-CN') {
|
||||
const percentage = target > 0 ? (numericValue / target) * 100 : 0;
|
||||
const remaining = target - numericValue;
|
||||
|
||||
tooltipMessage = `${numericValue.toFixed(1)} g ${label.toLowerCase()} • 占 ${Math.round(percentage)}%`;
|
||||
if (remaining > 0) {
|
||||
tooltipMessage += ` • 剩余 ${remaining.toFixed(1)} g`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipMessage += ` • 超出 ${Math.abs(remaining).toFixed(1)} g`;
|
||||
}
|
||||
} else {
|
||||
tooltipMessage = formatDashboardTooltip(numericValue, target, label);
|
||||
}
|
||||
}
|
||||
|
||||
safeAttachTooltip(card, tooltipMessage, this.plugin);
|
||||
|
|
|
|||
|
|
@ -83,11 +83,11 @@ export class ManualFoodEntryModal extends Modal {
|
|||
// Energy Fields Container (kcal and kJ side by side)
|
||||
const energyContainer = nutritionContainer.createDiv({ cls: 'energy-fields-container' });
|
||||
|
||||
// Calories Field
|
||||
// Calories Field with bold label
|
||||
const caloriesGroup = energyContainer.createDiv({ cls: 'form-group energy-field' });
|
||||
caloriesGroup.createEl('label', {
|
||||
text: t('food.manual.calories'),
|
||||
cls: 'form-label required',
|
||||
cls: 'form-label required energy-label-bold',
|
||||
});
|
||||
this.caloriesInput = caloriesGroup.createEl('input', {
|
||||
type: 'number',
|
||||
|
|
@ -100,11 +100,11 @@ export class ManualFoodEntryModal extends Modal {
|
|||
},
|
||||
});
|
||||
|
||||
// kJ Field
|
||||
// kJ Field with bold label
|
||||
const kjGroup = energyContainer.createDiv({ cls: 'form-group energy-field' });
|
||||
kjGroup.createEl('label', {
|
||||
text: t('food.manual.energy') + ' (kJ)',
|
||||
cls: 'form-label',
|
||||
cls: 'form-label energy-label-bold',
|
||||
});
|
||||
this.kjInput = kjGroup.createEl('input', {
|
||||
type: 'number',
|
||||
|
|
|
|||
|
|
@ -20,14 +20,25 @@ function getCurrentEnergyUnit(): 'kcal' | 'kJ' {
|
|||
return formatterPlugin?.settings?.energyUnit || 'kcal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current locale from the plugin
|
||||
*/
|
||||
function getCurrentLocale(): string {
|
||||
try {
|
||||
return formatterPlugin?.i18nManager?.getCurrentLocale() || 'en';
|
||||
} catch {
|
||||
return 'en';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format calories with proper unit conversion based on settings
|
||||
* @param calories Calorie value in kcal
|
||||
* @returns Formatted string with appropriate unit
|
||||
* @returns Formatted string with appropriate unit (with proper spacing)
|
||||
*/
|
||||
export function formatCalories(calories: number): string {
|
||||
if (!formatterPlugin) {
|
||||
// Fallback if plugin not set
|
||||
// Fallback if plugin not set - with proper spacing
|
||||
return `${calories.toFixed(1)} kcal`;
|
||||
}
|
||||
|
||||
|
|
@ -35,19 +46,38 @@ export function formatCalories(calories: number): string {
|
|||
|
||||
if (currentUnit === 'kJ') {
|
||||
const kj = convertEnergyUnit(calories, 'kcal', 'kJ');
|
||||
return `${kj.toFixed(1)} kJ`;
|
||||
return `${kj.toFixed(1)} kJ`; // Proper spacing (already correct)
|
||||
} else {
|
||||
return `${calories.toFixed(1)} kcal`;
|
||||
return `${calories.toFixed(1)} kcal`; // Proper spacing (already correct)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format grams with one decimal place
|
||||
* Format grams with one decimal place and proper spacing
|
||||
* @param grams Gram value
|
||||
* @returns Formatted string
|
||||
* @returns Formatted string with space between value and unit
|
||||
*/
|
||||
export function formatGrams(grams: number): string {
|
||||
return `${grams.toFixed(1)}g`;
|
||||
return `${grams.toFixed(1)} g`; // Added space for consistency with energy units
|
||||
}
|
||||
|
||||
/**
|
||||
* Format weight with proper spacing (following NIST standards)
|
||||
* @param weight Weight value in grams
|
||||
* @returns Formatted string with space between value and unit
|
||||
*/
|
||||
export function formatWeight(weight: number): string {
|
||||
return `${weight.toFixed(1)} g`; // Added space between number and unit
|
||||
}
|
||||
|
||||
/**
|
||||
* Format macronutrient values with proper spacing
|
||||
* @param value Numeric value
|
||||
* @param unit Unit string (default: 'g')
|
||||
* @returns Formatted string with space between value and unit
|
||||
*/
|
||||
export function formatMacro(value: number, unit: string = 'g'): string {
|
||||
return `${value.toFixed(1)} ${unit}`; // Added space between number and unit
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,12 +89,34 @@ export function formatPercentage(percentage: number): string {
|
|||
return Math.round(percentage).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format tooltip text with locale-specific word order and proper spacing
|
||||
* Chinese: 占 7% · 剩余 95.2 g
|
||||
* Others: 7% of target · 95.2 g remaining
|
||||
*/
|
||||
export function formatTooltipText(
|
||||
percentage: number,
|
||||
remaining: number,
|
||||
unit: string,
|
||||
locale?: string
|
||||
): string {
|
||||
const currentLocale = locale || getCurrentLocale();
|
||||
|
||||
if (currentLocale === 'zh-CN') {
|
||||
// Chinese word order with proper spacing: 占 7% · 剩余 95.2 g
|
||||
return `占 ${Math.round(percentage)}% · 剩余 ${remaining.toFixed(1)} ${unit}`;
|
||||
} else {
|
||||
// Default word order with proper spacing: 7% of target · 95.2 g remaining
|
||||
return `${Math.round(percentage)}% ${t('tooltips.dailyTarget')} · ${remaining.toFixed(1)} ${unit} ${t('tooltips.remaining')}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tooltip text for dashboard metric cards
|
||||
* @param currentValue Current consumed value
|
||||
* @param targetValue Target value
|
||||
* @param macroName Name of the macro (for translation)
|
||||
* @returns Formatted tooltip string
|
||||
* @returns Formatted tooltip string with proper spacing
|
||||
*/
|
||||
export function formatDashboardTooltip(
|
||||
currentValue: number,
|
||||
|
|
@ -73,16 +125,31 @@ export function formatDashboardTooltip(
|
|||
): string {
|
||||
const percentage = targetValue > 0 ? (currentValue / targetValue) * 100 : 0;
|
||||
const remaining = targetValue - currentValue;
|
||||
const currentLocale = getCurrentLocale();
|
||||
|
||||
let tooltipText = `${currentValue.toFixed(1)}g ${macroName.toLowerCase()} • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
// Chinese word order: 占 7% · 剩余 95.2 g
|
||||
let tooltipText = `${currentValue.toFixed(1)} g ${macroName.toLowerCase()} • 占 ${Math.round(percentage)}%`;
|
||||
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • ${remaining.toFixed(1)}g ${t('general.remaining')}`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • ${Math.abs(remaining).toFixed(1)}g ${t('table.summary.over')}`;
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • 剩余 ${remaining.toFixed(1)} g`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • 超出 ${Math.abs(remaining).toFixed(1)} g`;
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
} else {
|
||||
// English word order
|
||||
let tooltipText = `${currentValue.toFixed(1)} g ${macroName.toLowerCase()} • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • ${remaining.toFixed(1)} g ${t('general.remaining')}`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • ${Math.abs(remaining).toFixed(1)} g ${t('table.summary.over')}`;
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,15 +230,15 @@ export function getSummaryHeader(id: string): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Format calories for tooltips with proper unit handling
|
||||
* Format calories for tooltips with proper unit handling and spacing
|
||||
* @param calories Calorie value in kcal
|
||||
* @param target Target calorie value in kcal
|
||||
* @param percentage Percentage of target
|
||||
* @returns Formatted tooltip string
|
||||
* @returns Formatted tooltip string with proper spacing
|
||||
*/
|
||||
export function formatCalorieTooltip(calories: number, target: number, percentage: number): string {
|
||||
if (!formatterPlugin) {
|
||||
// Fallback if plugin not set
|
||||
// Fallback if plugin not set - with proper spacing
|
||||
const remaining = target - calories;
|
||||
if (remaining > 0) {
|
||||
return `${calories.toFixed(1)} kcal • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')} • ${remaining.toFixed(1)} kcal ${t('general.remaining')}`;
|
||||
|
|
@ -181,33 +248,50 @@ export function formatCalorieTooltip(calories: number, target: number, percentag
|
|||
}
|
||||
|
||||
const currentUnit = getCurrentEnergyUnit();
|
||||
const currentLocale = getCurrentLocale();
|
||||
|
||||
if (currentUnit === 'kJ') {
|
||||
const consumedKj = convertEnergyUnit(calories, 'kcal', 'kJ');
|
||||
const targetKj = convertEnergyUnit(target, 'kcal', 'kJ');
|
||||
const remainingKj = targetKj - consumedKj;
|
||||
|
||||
let tooltipText = `${consumedKj.toFixed(1)} kJ • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
|
||||
if (remainingKj > 0) {
|
||||
tooltipText += ` • ${remainingKj.toFixed(1)} kJ ${t('general.remaining')}`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipText += ` • ${Math.abs(remainingKj).toFixed(1)} kJ ${t('table.summary.over')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
let tooltipText = `${consumedKj.toFixed(1)} kJ • 占 ${Math.round(percentage)}%`;
|
||||
if (remainingKj > 0) {
|
||||
tooltipText += ` • 剩余 ${remainingKj.toFixed(1)} kJ`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipText += ` • 超出 ${Math.abs(remainingKj).toFixed(1)} kJ`;
|
||||
}
|
||||
return tooltipText;
|
||||
} else {
|
||||
let tooltipText = `${consumedKj.toFixed(1)} kJ • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
if (remainingKj > 0) {
|
||||
tooltipText += ` • ${remainingKj.toFixed(1)} kJ ${t('general.remaining')}`;
|
||||
} else if (remainingKj < 0) {
|
||||
tooltipText += ` • ${Math.abs(remainingKj).toFixed(1)} kJ ${t('table.summary.over')}`;
|
||||
}
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
} else {
|
||||
const remaining = target - calories;
|
||||
|
||||
let tooltipText = `${calories.toFixed(1)} kcal • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • ${remaining.toFixed(1)} kcal ${t('general.remaining')}`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • ${Math.abs(remaining).toFixed(1)} kcal ${t('table.summary.over')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
let tooltipText = `${calories.toFixed(1)} kcal • 占 ${Math.round(percentage)}%`;
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • 剩余 ${remaining.toFixed(1)} kcal`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • 超出 ${Math.abs(remaining).toFixed(1)} kcal`;
|
||||
}
|
||||
return tooltipText;
|
||||
} else {
|
||||
let tooltipText = `${calories.toFixed(1)} kcal • ${Math.round(percentage)}% ${t('table.summary.dailyTarget')}`;
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • ${remaining.toFixed(1)} kcal ${t('general.remaining')}`;
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • ${Math.abs(remaining).toFixed(1)} kcal ${t('table.summary.over')}`;
|
||||
}
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +300,7 @@ export function formatCalorieTooltip(calories: number, target: number, percentag
|
|||
* @param macro Macro name
|
||||
* @param value Macro value in grams
|
||||
* @param percentage Percentage of total macros
|
||||
* @returns Formatted tooltip string
|
||||
* @returns Formatted tooltip string with proper spacing
|
||||
*/
|
||||
export function formatMacroCompositionTooltip(
|
||||
macro: string,
|
||||
|
|
@ -234,7 +318,7 @@ export function formatMacroCompositionTooltip(
|
|||
* Format target tooltip
|
||||
* @param target Target value
|
||||
* @param unit Unit (g, kcal, kJ)
|
||||
* @returns Formatted tooltip string
|
||||
* @returns Formatted tooltip string with proper spacing
|
||||
*/
|
||||
export function formatTargetTooltip(target: number, unit: string): string {
|
||||
return t('tooltips.target', {
|
||||
|
|
@ -244,12 +328,12 @@ export function formatTargetTooltip(target: number, unit: string): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Format percentage tooltip for macro cells
|
||||
* Format percentage tooltip for macro cells with proper spacing
|
||||
* @param value Macro value in grams
|
||||
* @param macro Macro name
|
||||
* @param percentage Percentage of daily target
|
||||
* @param target Daily target value
|
||||
* @returns Formatted tooltip string
|
||||
* @returns Formatted tooltip string with proper spacing
|
||||
*/
|
||||
export function formatMacroPercentageTooltip(
|
||||
value: number,
|
||||
|
|
@ -258,6 +342,7 @@ export function formatMacroPercentageTooltip(
|
|||
target: number
|
||||
): string {
|
||||
const remaining = target - value;
|
||||
const currentLocale = getCurrentLocale();
|
||||
|
||||
let tooltipText = t('tooltips.percentage', {
|
||||
value: value.toFixed(1),
|
||||
|
|
@ -266,19 +351,27 @@ export function formatMacroPercentageTooltip(
|
|||
});
|
||||
|
||||
if (remaining > 0) {
|
||||
tooltipText += ` • ${remaining.toFixed(1)}g ${t('general.remaining')}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
tooltipText += ` • 剩余 ${remaining.toFixed(1)} g`;
|
||||
} else {
|
||||
tooltipText += ` • ${remaining.toFixed(1)} g ${t('general.remaining')}`;
|
||||
}
|
||||
} else if (remaining < 0) {
|
||||
tooltipText += ` • ${t('tooltips.over', { over: Math.abs(remaining).toFixed(1) })}`;
|
||||
if (currentLocale === 'zh-CN') {
|
||||
tooltipText += ` • 超出 ${Math.abs(remaining).toFixed(1)} g`;
|
||||
} else {
|
||||
tooltipText += ` • ${t('tooltips.over', { over: Math.abs(remaining).toFixed(1) })}`;
|
||||
}
|
||||
}
|
||||
|
||||
return tooltipText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format energy value with automatic unit conversion
|
||||
* Format energy value with automatic unit conversion and proper spacing
|
||||
* @param valueInKcal Energy value in kcal
|
||||
* @param showUnit Whether to include the unit in the output
|
||||
* @returns Formatted energy string
|
||||
* @returns Formatted energy string with proper spacing
|
||||
*/
|
||||
export function formatEnergy(valueInKcal: number, showUnit: boolean = true): string {
|
||||
if (!formatterPlugin) {
|
||||
|
|
@ -289,12 +382,22 @@ export function formatEnergy(valueInKcal: number, showUnit: boolean = true): str
|
|||
|
||||
if (currentUnit === 'kJ') {
|
||||
const kj = convertEnergyUnit(valueInKcal, 'kcal', 'kJ');
|
||||
return showUnit ? `${kj.toFixed(1)} kJ` : kj.toFixed(1);
|
||||
return showUnit ? `${kj.toFixed(1)} kJ` : kj.toFixed(1); // Proper spacing
|
||||
} else {
|
||||
return showUnit ? `${valueInKcal.toFixed(1)} kcal` : valueInKcal.toFixed(1);
|
||||
return showUnit ? `${valueInKcal.toFixed(1)} kcal` : valueInKcal.toFixed(1); // Proper spacing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format serving size with proper spacing
|
||||
* @param serving Serving size string
|
||||
* @returns Formatted serving size with space between number and unit
|
||||
*/
|
||||
export function formatServing(serving: string): string {
|
||||
// Handle various serving size formats and ensure proper spacing
|
||||
return serving.replace(/(\d+(?:\.\d+)?)\s*g/i, '$1 g');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current energy unit as a string
|
||||
* @returns Current energy unit setting
|
||||
|
|
@ -315,11 +418,11 @@ export function formatNumber(value: number, decimals: number = 1): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Format meal summary text
|
||||
* Format meal summary text with proper spacing
|
||||
* @param mealName Name of the meal
|
||||
* @param itemCount Number of items in the meal
|
||||
* @param calories Total calories
|
||||
* @returns Formatted meal summary
|
||||
* @returns Formatted meal summary with proper spacing
|
||||
*/
|
||||
export function formatMealSummary(mealName: string, itemCount: number, calories: number): string {
|
||||
const calorieText = formatCalories(calories);
|
||||
|
|
@ -328,18 +431,18 @@ export function formatMealSummary(mealName: string, itemCount: number, calories:
|
|||
}
|
||||
|
||||
/**
|
||||
* Format remaining/over values for summary rows
|
||||
* Format remaining/over values for summary rows with proper spacing
|
||||
* @param remaining Remaining value (negative if over target)
|
||||
* @param unit Unit string
|
||||
* @returns Formatted remaining/over string
|
||||
* @returns Formatted remaining/over string with proper spacing
|
||||
*/
|
||||
export function formatRemaining(remaining: number, unit: string): string {
|
||||
if (remaining < 0) {
|
||||
return `${formatNumber(Math.abs(remaining))}${unit} (${t('table.summary.over')})`;
|
||||
return `${formatNumber(Math.abs(remaining))} ${unit} (${t('table.summary.over')})`;
|
||||
} else if (remaining === 0) {
|
||||
return `0${unit}`;
|
||||
return `0 ${unit}`;
|
||||
} else {
|
||||
return `${formatNumber(remaining)}${unit}`;
|
||||
return `${formatNumber(remaining)} ${unit}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2073
styles.css
2073
styles.css
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue