diff --git a/components/smart_display/runtime_tiles.h b/components/smart_display/runtime_tiles.h
index b761479..169d288 100644
--- a/components/smart_display/runtime_tiles.h
+++ b/components/smart_display/runtime_tiles.h
@@ -99,7 +99,7 @@ inline void refresh_detail(unsigned index);
inline const char *weather_icon(const std::string &condition);
inline const char *weather_text(const std::string &condition);
inline std::string timer_text(const Tile &t);
-inline std::string last_run_text(uint32_t epoch);
+inline std::string last_run_text(uint32_t epoch, bool compact = false);
// "07:12": a time of day as Home Assistant and ESP Screens send it, for screen_text::clock_text.
inline std::string hhmm(const esphome::ESPTime &time) {
char b[8]; snprintf(b, sizeof(b), "%02d:%02d", time.hour, time.minute);
@@ -1204,6 +1204,31 @@ inline lv_obj_t *detail_shape(lv_obj_t *parent,int x,int y,int w,int h,theme::Ro
inline int text_width(const std::string &text,const lv_font_t *font){
lv_point_t size;lv_text_get_size(&size,text.c_str(),font,0,0,LV_COORD_MAX,LV_TEXT_FLAG_NONE);return size.x;
}
+// The status line under a tile's name, once its room is known. LVGL cuts the end of a line that doesn't fit, which
+// is where this line carries its news, so (firmware 0.2.76+):
+// `shorter` another wording of the whole line, taken when the full one doesn't fit ("Yest. 9:15 PM");
+// `tail` the end that has to stay readable (a robot's battery): the words in front of it take the dots,
+// "Angedockt / 10 %" becomes "Angedo… / 10 %".
+// A line that fits, or a room too small for the tail itself, is left to LVGL as before.
+inline void fit_value(lv_obj_t *obj,const std::string &value,const std::string &shorter,const std::string &tail,int room){
+ const lv_font_t *font=lv_obj_get_style_text_font(obj,LV_PART_MAIN);
+ if(!font||room<=0||text_width(value,font)<=room)return;
+ if(!shorter.empty()&&shorter!=value&&text_width(shorter,font)<=room){label(obj,shorter);return;}
+ if(tail.empty()||value.size()<=tail.size())return;
+ static const std::string dots="…";
+ const int head_room=room-text_width(dots+tail,font);
+ if(head_room<=0)return;
+ const std::string head=value.substr(0,value.size()-tail.size());
+ std::string cut;
+ for(size_t i=0;i
(head[next])&0xC0)==0x80)++next;
+ if(text_width(head.substr(0,next),font)>head_room)break;
+ cut=head.substr(0,next);i=next;
+ }
+ while(!cut.empty()&&cut.back()==' ')cut.pop_back();
+ label(obj,cut+dots+tail);
+}
// The state colour, as Home Assistant colours a vacuum: teal while cleaning, blue on the way back, amber
// when paused, red on an error; a docked or idle robot keeps the card's own blue. `tint` is the halo.
struct VacuumLook { uint32_t accent, tint; };
@@ -2187,16 +2212,18 @@ inline std::string countdown(uint32_t seconds) {
return b;
}
// "Last 14:32" today, "Yesterday 14:32", else "Last 13 Sep", in the screen's language; scripts and scenes have no
-// useful on/off.
+// useful on/off. `compact` takes the short wording of yesterday a tile has room for ("Yest. 9:15 PM"); every
+// language that needs no shorter word keeps the same text there.
inline std::string month_short(const esphome::ESPTime &now);
-inline std::string last_run_text(uint32_t epoch) {
+inline std::string last_run_text(uint32_t epoch, bool compact) {
if (!epoch) return tr(txt::script_never_run);
auto when = esphome::ESPTime::from_epoch_local(epoch);
auto now = now_time ? now_time() : esphome::ESPTime{};
if (!when.is_valid()) return tr(txt::script_never_run);
std::string clock = screen_text::clock_text(hhmm(when), screen_settings::current.clock_24h != 0);
if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year) return fill(txt::script_last_time, "time", clock);
- if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year + 1) return fill(txt::script_yesterday_time, "time", clock);
+ if (now.is_valid() && now.year == when.year && now.day_of_year == when.day_of_year + 1)
+ return fill(compact ? txt::script_yesterday_time_short : txt::script_yesterday_time, "time", clock);
std::string date = fill(fill(txt::date_day_month, "day", std::to_string(when.day_of_month)), "month", month_short(when));
return fill(txt::script_last_date, "date", date);
}
@@ -3168,10 +3195,14 @@ inline void render_slot(size_t slot) {
else if (!t.unit.empty() || d == "number" || d == "input_number" || d == "counter") value = screen_text::localize(value);
bool pending=t.loading(esphome::millis());
if(d=="weather" && std::isfinite(t.current)) {value=screen_text::decimal(t.current,1);if(!watch)value=screen_text::with_unit(value,t.unit);}
- if(d=="vacuum" && std::isfinite(t.battery))value += " / "+screen_text::percent((int)t.battery);
+ // What a narrow tile falls back to once its room is known (fit_value): another wording of the whole line, and the
+ // end that must stay readable whatever happens to the words in front of it.
+ std::string value_short,value_tail;
+ if((d=="script"||d=="scene"||d=="button"||d=="input_button") && t.state!="on")value_short=last_run_text(t.last_run,true);
+ if(d=="vacuum" && std::isfinite(t.battery)){value_tail=" / "+screen_text::percent((int)t.battery);value+=value_tail;}
// Direct controls: only a wide card in the standard layout has room for the panel.
bool with_panel=w.wide && !t.controls.empty() && !t.builtin() && !watch && t.inline_control!="slider" && fresh() && t.available();
- if(with_panel){std::string status=tile_controls::status_text(t);if(!status.empty())value=status;}
+ if(with_panel){std::string status=tile_controls::status_text(t);if(!status.empty()){value=status;value_short.clear();value_tail.clear();}}
label(w.value, value);
bool mini=t.inline_control=="slider" && !watch && t.available();
bool large_tile=tile_height(w)>80;
@@ -3239,7 +3270,9 @@ inline void render_slot(size_t slot) {
int chevron_w=t.is_page() && !watch?lv_font_get_line_height(chevron_font):0;
if(chevron_w)text_room-=chevron_w+(large_tile?8:4);
lv_obj_set_width(w.title,std::max(1,text_room-text_x));
- lv_obj_set_width(w.value,std::max(1,text_room-(watch?0:(mini||graph_strip)?text_x:w.value_x)));
+ int value_room=std::max(1,text_room-(watch?0:(mini||graph_strip)?text_x:w.value_x));
+ lv_obj_set_width(w.value,value_room);
+ fit_value(w.value,value,value_short,value_tail,value_room);
if(watch){
int gap=large_tile?6:2,header=std::max(circle_size,title_height);
int group_y=std::max(0,(content_h-header-gap-value_height)/2);
diff --git a/components/smart_display/screen_text_keys.h b/components/smart_display/screen_text_keys.h
index 88395fe..a882d7b 100644
--- a/components/smart_display/screen_text_keys.h
+++ b/components/smart_display/screen_text_keys.h
@@ -144,144 +144,145 @@ constexpr uint16_t script_running = 179;
constexpr uint16_t script_never_run = 180;
constexpr uint16_t script_last_time = 181;
constexpr uint16_t script_yesterday_time = 182;
-constexpr uint16_t script_last_date = 183;
-constexpr uint16_t camera_no_image = 184;
-constexpr uint16_t camera_loading = 185;
-constexpr uint16_t camera_live = 186;
-constexpr uint16_t camera_recording = 187;
-constexpr uint16_t camera_tap_to_view = 188;
-constexpr uint16_t camera_no_image_yet = 189;
-constexpr uint16_t calibration_title = 190;
-constexpr uint16_t calibration_touch_check = 191;
-constexpr uint16_t calibration_hold = 192;
-constexpr uint16_t calibration_crosshair = 193;
-constexpr uint16_t calibration_gently = 194;
-constexpr uint16_t calibration_same = 195;
-constexpr uint16_t calibration_rejected = 196;
-constexpr uint16_t calibration_save_failed = 197;
-constexpr uint16_t ha_on = 198;
-constexpr uint16_t ha_off = 199;
-constexpr uint16_t ha_unavailable = 200;
-constexpr uint16_t ha_climate_off = 201;
-constexpr uint16_t ha_climate_heat = 202;
-constexpr uint16_t ha_climate_cool = 203;
-constexpr uint16_t ha_climate_heat_cool = 204;
-constexpr uint16_t ha_climate_auto = 205;
-constexpr uint16_t ha_climate_dry = 206;
-constexpr uint16_t ha_climate_fan_only = 207;
-constexpr uint16_t ha_hvac_action_heating = 208;
-constexpr uint16_t ha_hvac_action_cooling = 209;
-constexpr uint16_t ha_hvac_action_idle = 210;
-constexpr uint16_t ha_hvac_action_off = 211;
-constexpr uint16_t ha_hvac_action_drying = 212;
-constexpr uint16_t ha_hvac_action_fan = 213;
-constexpr uint16_t ha_hvac_action_preheating = 214;
-constexpr uint16_t ha_hvac_action_defrosting = 215;
-constexpr uint16_t ha_cover_open = 216;
-constexpr uint16_t ha_cover_closed = 217;
-constexpr uint16_t ha_cover_opening = 218;
-constexpr uint16_t ha_cover_closing = 219;
-constexpr uint16_t ha_media_playing = 220;
-constexpr uint16_t ha_media_paused = 221;
-constexpr uint16_t ha_media_idle = 222;
-constexpr uint16_t ha_media_standby = 223;
-constexpr uint16_t ha_person_home = 224;
-constexpr uint16_t ha_person_not_home = 225;
-constexpr uint16_t ha_sun_above_horizon = 226;
-constexpr uint16_t ha_sun_below_horizon = 227;
-constexpr uint16_t ha_vacuum_docked = 228;
-constexpr uint16_t ha_vacuum_cleaning = 229;
-constexpr uint16_t ha_vacuum_paused = 230;
-constexpr uint16_t ha_vacuum_returning = 231;
-constexpr uint16_t ha_vacuum_idle = 232;
-constexpr uint16_t ha_weather_sunny = 233;
-constexpr uint16_t ha_weather_clear_night = 234;
-constexpr uint16_t ha_weather_cloudy = 235;
-constexpr uint16_t ha_weather_partlycloudy = 236;
-constexpr uint16_t ha_weather_rainy = 237;
-constexpr uint16_t ha_weather_pouring = 238;
-constexpr uint16_t ha_weather_snowy = 239;
-constexpr uint16_t ha_weather_snowy_rainy = 240;
-constexpr uint16_t ha_weather_fog = 241;
-constexpr uint16_t ha_weather_hail = 242;
-constexpr uint16_t ha_weather_lightning = 243;
-constexpr uint16_t ha_weather_lightning_rainy = 244;
-constexpr uint16_t ha_weather_windy = 245;
-constexpr uint16_t ha_weather_exceptional = 246;
-constexpr uint16_t ha_binary_battery_on = 247;
-constexpr uint16_t ha_binary_battery_off = 248;
-constexpr uint16_t ha_binary_battery_charging_on = 249;
-constexpr uint16_t ha_binary_battery_charging_off = 250;
-constexpr uint16_t ha_binary_carbon_monoxide_on = 251;
-constexpr uint16_t ha_binary_carbon_monoxide_off = 252;
-constexpr uint16_t ha_binary_cold_on = 253;
-constexpr uint16_t ha_binary_cold_off = 254;
-constexpr uint16_t ha_binary_connectivity_on = 255;
-constexpr uint16_t ha_binary_connectivity_off = 256;
-constexpr uint16_t ha_binary_door_on = 257;
-constexpr uint16_t ha_binary_door_off = 258;
-constexpr uint16_t ha_binary_garage_door_on = 259;
-constexpr uint16_t ha_binary_garage_door_off = 260;
-constexpr uint16_t ha_binary_gas_on = 261;
-constexpr uint16_t ha_binary_gas_off = 262;
-constexpr uint16_t ha_binary_heat_on = 263;
-constexpr uint16_t ha_binary_heat_off = 264;
-constexpr uint16_t ha_binary_light_on = 265;
-constexpr uint16_t ha_binary_light_off = 266;
-constexpr uint16_t ha_binary_lock_on = 267;
-constexpr uint16_t ha_binary_lock_off = 268;
-constexpr uint16_t ha_binary_moisture_on = 269;
-constexpr uint16_t ha_binary_moisture_off = 270;
-constexpr uint16_t ha_binary_motion_on = 271;
-constexpr uint16_t ha_binary_motion_off = 272;
-constexpr uint16_t ha_binary_moving_on = 273;
-constexpr uint16_t ha_binary_moving_off = 274;
-constexpr uint16_t ha_binary_occupancy_on = 275;
-constexpr uint16_t ha_binary_occupancy_off = 276;
-constexpr uint16_t ha_binary_opening_on = 277;
-constexpr uint16_t ha_binary_opening_off = 278;
-constexpr uint16_t ha_binary_plug_on = 279;
-constexpr uint16_t ha_binary_plug_off = 280;
-constexpr uint16_t ha_binary_power_on = 281;
-constexpr uint16_t ha_binary_power_off = 282;
-constexpr uint16_t ha_binary_presence_on = 283;
-constexpr uint16_t ha_binary_presence_off = 284;
-constexpr uint16_t ha_binary_problem_on = 285;
-constexpr uint16_t ha_binary_problem_off = 286;
-constexpr uint16_t ha_binary_running_on = 287;
-constexpr uint16_t ha_binary_running_off = 288;
-constexpr uint16_t ha_binary_safety_on = 289;
-constexpr uint16_t ha_binary_safety_off = 290;
-constexpr uint16_t ha_binary_smoke_on = 291;
-constexpr uint16_t ha_binary_smoke_off = 292;
-constexpr uint16_t ha_binary_sound_on = 293;
-constexpr uint16_t ha_binary_sound_off = 294;
-constexpr uint16_t ha_binary_tamper_on = 295;
-constexpr uint16_t ha_binary_tamper_off = 296;
-constexpr uint16_t ha_binary_update_on = 297;
-constexpr uint16_t ha_binary_update_off = 298;
-constexpr uint16_t ha_binary_vibration_on = 299;
-constexpr uint16_t ha_binary_vibration_off = 300;
-constexpr uint16_t ha_binary_window_on = 301;
-constexpr uint16_t ha_binary_window_off = 302;
-constexpr uint16_t ha_button_activate = 303;
-constexpr uint16_t ha_button_run = 304;
-constexpr uint16_t ha_button_press = 305;
-constexpr uint16_t ha_climate_fan_auto = 306;
-constexpr uint16_t ha_climate_fan_low = 307;
-constexpr uint16_t ha_climate_fan_medium = 308;
-constexpr uint16_t ha_climate_fan_high = 309;
-constexpr uint16_t ha_climate_fan_middle = 310;
-constexpr uint16_t ha_climate_fan_focus = 311;
-constexpr uint16_t ha_climate_fan_diffuse = 312;
-constexpr uint16_t ha_climate_fan_top = 313;
-constexpr uint16_t ha_climate_fan_on = 314;
-constexpr uint16_t ha_climate_fan_off = 315;
-constexpr uint16_t ha_climate_swing_on = 316;
-constexpr uint16_t ha_climate_swing_off = 317;
-constexpr uint16_t ha_climate_swing_both = 318;
-constexpr uint16_t ha_climate_swing_vertical = 319;
-constexpr uint16_t ha_climate_swing_horizontal = 320;
+constexpr uint16_t script_yesterday_time_short = 183;
+constexpr uint16_t script_last_date = 184;
+constexpr uint16_t camera_no_image = 185;
+constexpr uint16_t camera_loading = 186;
+constexpr uint16_t camera_live = 187;
+constexpr uint16_t camera_recording = 188;
+constexpr uint16_t camera_tap_to_view = 189;
+constexpr uint16_t camera_no_image_yet = 190;
+constexpr uint16_t calibration_title = 191;
+constexpr uint16_t calibration_touch_check = 192;
+constexpr uint16_t calibration_hold = 193;
+constexpr uint16_t calibration_crosshair = 194;
+constexpr uint16_t calibration_gently = 195;
+constexpr uint16_t calibration_same = 196;
+constexpr uint16_t calibration_rejected = 197;
+constexpr uint16_t calibration_save_failed = 198;
+constexpr uint16_t ha_on = 199;
+constexpr uint16_t ha_off = 200;
+constexpr uint16_t ha_unavailable = 201;
+constexpr uint16_t ha_climate_off = 202;
+constexpr uint16_t ha_climate_heat = 203;
+constexpr uint16_t ha_climate_cool = 204;
+constexpr uint16_t ha_climate_heat_cool = 205;
+constexpr uint16_t ha_climate_auto = 206;
+constexpr uint16_t ha_climate_dry = 207;
+constexpr uint16_t ha_climate_fan_only = 208;
+constexpr uint16_t ha_hvac_action_heating = 209;
+constexpr uint16_t ha_hvac_action_cooling = 210;
+constexpr uint16_t ha_hvac_action_idle = 211;
+constexpr uint16_t ha_hvac_action_off = 212;
+constexpr uint16_t ha_hvac_action_drying = 213;
+constexpr uint16_t ha_hvac_action_fan = 214;
+constexpr uint16_t ha_hvac_action_preheating = 215;
+constexpr uint16_t ha_hvac_action_defrosting = 216;
+constexpr uint16_t ha_cover_open = 217;
+constexpr uint16_t ha_cover_closed = 218;
+constexpr uint16_t ha_cover_opening = 219;
+constexpr uint16_t ha_cover_closing = 220;
+constexpr uint16_t ha_media_playing = 221;
+constexpr uint16_t ha_media_paused = 222;
+constexpr uint16_t ha_media_idle = 223;
+constexpr uint16_t ha_media_standby = 224;
+constexpr uint16_t ha_person_home = 225;
+constexpr uint16_t ha_person_not_home = 226;
+constexpr uint16_t ha_sun_above_horizon = 227;
+constexpr uint16_t ha_sun_below_horizon = 228;
+constexpr uint16_t ha_vacuum_docked = 229;
+constexpr uint16_t ha_vacuum_cleaning = 230;
+constexpr uint16_t ha_vacuum_paused = 231;
+constexpr uint16_t ha_vacuum_returning = 232;
+constexpr uint16_t ha_vacuum_idle = 233;
+constexpr uint16_t ha_weather_sunny = 234;
+constexpr uint16_t ha_weather_clear_night = 235;
+constexpr uint16_t ha_weather_cloudy = 236;
+constexpr uint16_t ha_weather_partlycloudy = 237;
+constexpr uint16_t ha_weather_rainy = 238;
+constexpr uint16_t ha_weather_pouring = 239;
+constexpr uint16_t ha_weather_snowy = 240;
+constexpr uint16_t ha_weather_snowy_rainy = 241;
+constexpr uint16_t ha_weather_fog = 242;
+constexpr uint16_t ha_weather_hail = 243;
+constexpr uint16_t ha_weather_lightning = 244;
+constexpr uint16_t ha_weather_lightning_rainy = 245;
+constexpr uint16_t ha_weather_windy = 246;
+constexpr uint16_t ha_weather_exceptional = 247;
+constexpr uint16_t ha_binary_battery_on = 248;
+constexpr uint16_t ha_binary_battery_off = 249;
+constexpr uint16_t ha_binary_battery_charging_on = 250;
+constexpr uint16_t ha_binary_battery_charging_off = 251;
+constexpr uint16_t ha_binary_carbon_monoxide_on = 252;
+constexpr uint16_t ha_binary_carbon_monoxide_off = 253;
+constexpr uint16_t ha_binary_cold_on = 254;
+constexpr uint16_t ha_binary_cold_off = 255;
+constexpr uint16_t ha_binary_connectivity_on = 256;
+constexpr uint16_t ha_binary_connectivity_off = 257;
+constexpr uint16_t ha_binary_door_on = 258;
+constexpr uint16_t ha_binary_door_off = 259;
+constexpr uint16_t ha_binary_garage_door_on = 260;
+constexpr uint16_t ha_binary_garage_door_off = 261;
+constexpr uint16_t ha_binary_gas_on = 262;
+constexpr uint16_t ha_binary_gas_off = 263;
+constexpr uint16_t ha_binary_heat_on = 264;
+constexpr uint16_t ha_binary_heat_off = 265;
+constexpr uint16_t ha_binary_light_on = 266;
+constexpr uint16_t ha_binary_light_off = 267;
+constexpr uint16_t ha_binary_lock_on = 268;
+constexpr uint16_t ha_binary_lock_off = 269;
+constexpr uint16_t ha_binary_moisture_on = 270;
+constexpr uint16_t ha_binary_moisture_off = 271;
+constexpr uint16_t ha_binary_motion_on = 272;
+constexpr uint16_t ha_binary_motion_off = 273;
+constexpr uint16_t ha_binary_moving_on = 274;
+constexpr uint16_t ha_binary_moving_off = 275;
+constexpr uint16_t ha_binary_occupancy_on = 276;
+constexpr uint16_t ha_binary_occupancy_off = 277;
+constexpr uint16_t ha_binary_opening_on = 278;
+constexpr uint16_t ha_binary_opening_off = 279;
+constexpr uint16_t ha_binary_plug_on = 280;
+constexpr uint16_t ha_binary_plug_off = 281;
+constexpr uint16_t ha_binary_power_on = 282;
+constexpr uint16_t ha_binary_power_off = 283;
+constexpr uint16_t ha_binary_presence_on = 284;
+constexpr uint16_t ha_binary_presence_off = 285;
+constexpr uint16_t ha_binary_problem_on = 286;
+constexpr uint16_t ha_binary_problem_off = 287;
+constexpr uint16_t ha_binary_running_on = 288;
+constexpr uint16_t ha_binary_running_off = 289;
+constexpr uint16_t ha_binary_safety_on = 290;
+constexpr uint16_t ha_binary_safety_off = 291;
+constexpr uint16_t ha_binary_smoke_on = 292;
+constexpr uint16_t ha_binary_smoke_off = 293;
+constexpr uint16_t ha_binary_sound_on = 294;
+constexpr uint16_t ha_binary_sound_off = 295;
+constexpr uint16_t ha_binary_tamper_on = 296;
+constexpr uint16_t ha_binary_tamper_off = 297;
+constexpr uint16_t ha_binary_update_on = 298;
+constexpr uint16_t ha_binary_update_off = 299;
+constexpr uint16_t ha_binary_vibration_on = 300;
+constexpr uint16_t ha_binary_vibration_off = 301;
+constexpr uint16_t ha_binary_window_on = 302;
+constexpr uint16_t ha_binary_window_off = 303;
+constexpr uint16_t ha_button_activate = 304;
+constexpr uint16_t ha_button_run = 305;
+constexpr uint16_t ha_button_press = 306;
+constexpr uint16_t ha_climate_fan_auto = 307;
+constexpr uint16_t ha_climate_fan_low = 308;
+constexpr uint16_t ha_climate_fan_medium = 309;
+constexpr uint16_t ha_climate_fan_high = 310;
+constexpr uint16_t ha_climate_fan_middle = 311;
+constexpr uint16_t ha_climate_fan_focus = 312;
+constexpr uint16_t ha_climate_fan_diffuse = 313;
+constexpr uint16_t ha_climate_fan_top = 314;
+constexpr uint16_t ha_climate_fan_on = 315;
+constexpr uint16_t ha_climate_fan_off = 316;
+constexpr uint16_t ha_climate_swing_on = 317;
+constexpr uint16_t ha_climate_swing_off = 318;
+constexpr uint16_t ha_climate_swing_both = 319;
+constexpr uint16_t ha_climate_swing_vertical = 320;
+constexpr uint16_t ha_climate_swing_horizontal = 321;
constexpr uint16_t date_weekdays = 18;
constexpr uint16_t date_weekdays_count = 7;
constexpr uint16_t date_weekdays_short = 25;
@@ -293,6 +294,6 @@ constexpr uint16_t date_months_count = 12;
constexpr uint16_t date_months_short = 51;
constexpr uint16_t date_months_short_count = 12;
} // namespace txt
-constexpr uint16_t KEY_COUNT = 321;
-constexpr uint32_t KEYS_HASH = 0x8FB53847u;
+constexpr uint16_t KEY_COUNT = 322;
+constexpr uint32_t KEYS_HASH = 0x6D3912FFu;
} // namespace screen_text
diff --git a/screen_manager/translations/de.json b/screen_manager/translations/de.json
index a35d522..7031623 100644
--- a/screen_manager/translations/de.json
+++ b/screen_manager/translations/de.json
@@ -238,6 +238,7 @@
"never_run": "Nie ausgeführt",
"last_time": "Zuletzt {time}",
"yesterday_time": "Gestern {time}",
+ "yesterday_time_short": "Gestern {time}",
"last_date": "Zuletzt {date}"
},
"camera": {
diff --git a/screen_manager/translations/en.json b/screen_manager/translations/en.json
index 561ffc4..8c8e6b5 100644
--- a/screen_manager/translations/en.json
+++ b/screen_manager/translations/en.json
@@ -238,6 +238,7 @@
"never_run": "Never run",
"last_time": "Last {time}",
"yesterday_time": "Yesterday {time}",
+ "yesterday_time_short": "Yest. {time}",
"last_date": "Last {date}"
},
"camera": {
diff --git a/screen_manager/translations/es.json b/screen_manager/translations/es.json
index 2652bd2..7c16d5d 100644
--- a/screen_manager/translations/es.json
+++ b/screen_manager/translations/es.json
@@ -238,6 +238,7 @@
"never_run": "Nunca ejecutado",
"last_time": "Última {time}",
"yesterday_time": "Ayer {time}",
+ "yesterday_time_short": "Ayer {time}",
"last_date": "Última {date}"
},
"camera": {
diff --git a/screen_manager/translations/fr.json b/screen_manager/translations/fr.json
index 8e74665..e867b2a 100644
--- a/screen_manager/translations/fr.json
+++ b/screen_manager/translations/fr.json
@@ -238,6 +238,7 @@
"never_run": "Jamais lancé",
"last_time": "Dernier {time}",
"yesterday_time": "Hier {time}",
+ "yesterday_time_short": "Hier {time}",
"last_date": "Dernier {date}"
},
"camera": {
diff --git a/screen_manager/translations/it.json b/screen_manager/translations/it.json
index 0a5623a..ddd5617 100644
--- a/screen_manager/translations/it.json
+++ b/screen_manager/translations/it.json
@@ -238,6 +238,7 @@
"never_run": "Mai eseguito",
"last_time": "Ultima {time}",
"yesterday_time": "Ieri {time}",
+ "yesterday_time_short": "Ieri {time}",
"last_date": "Ultima {date}"
},
"camera": {
diff --git a/screen_manager/translations/nl.json b/screen_manager/translations/nl.json
index b501146..51e4453 100644
--- a/screen_manager/translations/nl.json
+++ b/screen_manager/translations/nl.json
@@ -238,6 +238,7 @@
"never_run": "Nooit uitgevoerd",
"last_time": "Laatst {time}",
"yesterday_time": "Gisteren {time}",
+ "yesterday_time_short": "Gisteren {time}",
"last_date": "Laatst {date}"
},
"camera": {
diff --git a/screen_manager/translations/pl.json b/screen_manager/translations/pl.json
index 8a6efe5..7ce46e6 100644
--- a/screen_manager/translations/pl.json
+++ b/screen_manager/translations/pl.json
@@ -238,6 +238,7 @@
"never_run": "Nie uruchamiano",
"last_time": "Ostatnio {time}",
"yesterday_time": "Wczoraj {time}",
+ "yesterday_time_short": "Wczoraj {time}",
"last_date": "Ostatnio {date}"
},
"camera": {
diff --git a/screen_manager/translations/pt.json b/screen_manager/translations/pt.json
index 66b91df..297a885 100644
--- a/screen_manager/translations/pt.json
+++ b/screen_manager/translations/pt.json
@@ -238,6 +238,7 @@
"never_run": "Nunca executado",
"last_time": "Última {time}",
"yesterday_time": "Ontem {time}",
+ "yesterday_time_short": "Ontem {time}",
"last_date": "Última {date}"
},
"camera": {
diff --git a/tests/screen_text_en.h b/tests/screen_text_en.h
index 4d009ff..684b31c 100644
--- a/tests/screen_text_en.h
+++ b/tests/screen_text_en.h
@@ -3,7 +3,7 @@
#pragma once
#include "components/smart_display/screen_text.h"
-static_assert(screen_text::KEYS_HASH == 0x8FB53847u && screen_text::KEY_COUNT == 321,
+static_assert(screen_text::KEYS_HASH == 0x6D3912FFu && screen_text::KEY_COUNT == 322,
"screen_text_keys.h does not match screen_manager/translations/en.json: run tools/i18n.py header");
const char *const screen_text::TABLE[] = {
".",
@@ -189,6 +189,7 @@ const char *const screen_text::TABLE[] = {
"Never run",
"Last {time}",
"Yesterday {time}",
+ "Yest. {time}",
"Last {date}",
"No image from this camera",
"Loading image",
diff --git a/tools/i18n.py b/tools/i18n.py
index 0bfafb1..51b76a3 100644
--- a/tools/i18n.py
+++ b/tools/i18n.py
@@ -133,10 +133,13 @@ def check():
if ours and code != 'en' and len(text) > max(8, len(source) * 1.6) and not plural:
notes.append(f'{code}: {key} is much longer than the English ({len(text)} vs {len(source)})')
if width and key in TILE_LINES:
+ shown_text = text
+ for name, sample in TILE_SAMPLES.items():
+ shown_text = shown_text.replace('{' + name + '}', sample)
for board, (size, room) in TILE_LINES[key].items():
- if width(size, text) > room:
- problems.append(f'{code}: {key} "{text}" is too wide for a tile on the {board} '
- f'({width(size, text):.0f} of {room} px); make it shorter')
+ if width(size, shown_text) > room:
+ problems.append(f'{code}: {key} "{shown_text}" is too wide for a tile on the {board} '
+ f'({width(size, shown_text):.0f} of {room} px); make it shorter')
# The weather columns of the CYD hold a day of two letters beside its icon.
if key.startswith('screen.date.weekdays_min.') and len(text) > 2:
problems.append(f'{code}: {key} "{text}" is longer than two letters; the CYD\'s weather columns cut it')
@@ -154,8 +157,12 @@ TILE_ROOM = {'CYD': (11, 88), 'Guition': (16, 128)}
# Camera tiles are the Guition's alone.
TILE_LINES = {'screen.tile.tap_to_open': TILE_ROOM, 'screen.script.never_run': TILE_ROOM, 'screen.script.running': TILE_ROOM,
'screen.media.not_playing': TILE_ROOM, 'screen.timer.paused': TILE_ROOM,
+ 'screen.script.last_time': TILE_ROOM, 'screen.script.yesterday_time_short': TILE_ROOM,
'screen.camera.tap_to_view': {'Guition': TILE_ROOM['Guition']},
'screen.camera.no_image_yet': {'Guition': TILE_ROOM['Guition']}}
+# What a placeholder stands for while a tile line is measured: the widest clock a screen writes is a 12-hour one
+# with its AM/PM ("Yesterday 9:15 PM" is why screen.script.yesterday_time_short exists).
+TILE_SAMPLES = {'time': '9:15 PM'}
def text_width():