diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e8291c..a425610 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ target_include_directories(imgui PUBLIC add_subdirectory(libs/libhydrogen EXCLUDE_FROM_ALL) add_executable(work-calendar + src/localization.c $<$:src/main_win32.cpp> $<$:src/main_linux.cpp> ) @@ -46,3 +47,8 @@ target_link_libraries(work-calendar glfw > ) + +target_compile_definitions(work-calendar + PRIVATE + $<$:_CRT_SECURE_NO_WARNINGS=1> +) diff --git a/src/localization.c b/src/localization.c new file mode 100644 index 0000000..4abf12b --- /dev/null +++ b/src/localization.c @@ -0,0 +1,99 @@ +#include "localization.h" + +const char *localized_strings_storage[LANGUAGE_COUNT][LOCALIZED_STRING_COUNT] = { + [L_GERMAN] = { + [LS_LANGUAGE_NAME] = "Deutsch", + [LS_JANUARY] = "Januar", + [LS_FEBRUARY] = "Februar", + [LS_MARCH] = "März", + [LS_APRIL] = "April", + [LS_MAY] = "Mai", + [LS_JUNE] = "Juni", + [LS_JULY] = "Juli", + [LS_AUGUST] = "August", + [LS_SEPTEMBER] = "September", + [LS_OCTOBER] = "Oktober", + [LS_NOVEMBER] = "November", + [LS_DECEMBER] = "Dezember", + [LS_FILE] = "Datei", + [LS_OPEN] = "Öffnen", + [LS_SAVE] = "Speichern", + [LS_SAVE_AS] = "Speichern unter", + [LS_CLOSE_CALENDAR] = "Kalender schließen", + [LS_EXIT] = "Beenden", + [LS_VIEW] = "Ansicht", + [LS_LEGEND] = "Legende", + [LS_LANGUAGE] = "Sprache", + [LS_HELP] = "Hilfe", + [LS_DEMO] = "Demo", + [LS_WIKI] = "Wiki", + [LS_ABOUT] = "Über", + [LS_CALENDAR_WEEK_SHORT] = "KW", + [LS_MONDAY_SHORT] = "Mo", + [LS_TUESDAY_SHORT] = "Di", + [LS_WEDNESDAY_SHORT] = "Mi", + [LS_THURSDAY_SHORT] = "Do", + [LS_FRIDAY_SHORT] = "Fr", + [LS_SATURDAY_SHORT] = "Sa", + [LS_SUNDAY_SHORT] = "So", + [LS_TITLE_ENTER_PASSWORD] = "Passwort eingeben###Save", + [LS_PASSWORD] = "Passwort", + [LS_CONFIRM_PASSWORD] = "Passwort bestätigen", + [LS_OK] = "Ok", + [LS_CANCEL] = "Abbrechen", + [LS_TITLE_OPEN_PASSWORD] = "Passwort eingeben###Open", + [LS_CLOSE] = "Schließen", + [LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT] = "Möchten Sie wirklich beenden?", + [LS_YES] = "Ja", + [LS_NO] = "Nein", + }, + [L_ENGLISH] = { + [LS_LANGUAGE_NAME] = "English", + [LS_JANUARY] = "January", + [LS_FEBRUARY] = "February", + [LS_MARCH] = "March", + [LS_APRIL] = "April", + [LS_MAY] = "May", + [LS_JUNE] = "June", + [LS_JULY] = "July", + [LS_AUGUST] = "August", + [LS_SEPTEMBER] = "September", + [LS_OCTOBER] = "October", + [LS_NOVEMBER] = "November", + [LS_DECEMBER] = "December", + [LS_FILE] = "File", + [LS_OPEN] = "Open", + [LS_SAVE] = "Save", + [LS_SAVE_AS] = "Save as", + [LS_CLOSE_CALENDAR] = "Close Calendar", + [LS_EXIT] = "Exit", + [LS_VIEW] = "View", + [LS_LEGEND] = "Legend", + [LS_LANGUAGE] = "Language", + [LS_HELP] = "Help", + [LS_DEMO] = "Demo", + [LS_WIKI] = "Wiki", + [LS_ABOUT] = "About", + [LS_CALENDAR_WEEK_SHORT] = "No.", + [LS_MONDAY_SHORT] = "Mo", + [LS_TUESDAY_SHORT] = "Tu", + [LS_WEDNESDAY_SHORT] = "We", + [LS_THURSDAY_SHORT] = "Th", + [LS_FRIDAY_SHORT] = "Fr", + [LS_SATURDAY_SHORT] = "Sa", + [LS_SUNDAY_SHORT] = "Su", + [LS_TITLE_ENTER_PASSWORD] = "Enter Password###Save", + [LS_PASSWORD] = "Password", + [LS_CONFIRM_PASSWORD] = "Confirm Password", + [LS_OK] = "Ok", + [LS_CANCEL] = "Cancel", + [LS_TITLE_OPEN_PASSWORD] = "Enter Password###Open", + [LS_CLOSE] = "Close", + [LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT] = "Do you really want to quit?", + [LS_YES] = "Yes", + [LS_NO] = "No", + }, + [L_FRENCH] = { + [LS_LANGUAGE_NAME] = "Français", + } +}; \ No newline at end of file diff --git a/src/localization.h b/src/localization.h new file mode 100644 index 0000000..626ea1f --- /dev/null +++ b/src/localization.h @@ -0,0 +1,66 @@ +#pragma once + +enum Localized_String { + LS_JANUARY, + LS_FEBRUARY, + LS_MARCH, + LS_APRIL, + LS_MAY, + LS_JUNE, + LS_JULY, + LS_AUGUST, + LS_SEPTEMBER, + LS_OCTOBER, + LS_NOVEMBER, + LS_DECEMBER, + LS_LANGUAGE_NAME, + LS_FILE, + LS_OPEN, + LS_SAVE, + LS_SAVE_AS, + LS_CLOSE_CALENDAR, + LS_EXIT, + LS_VIEW, + LS_LEGEND, + LS_LANGUAGE, + LS_HELP, + LS_DEMO, + LS_WIKI, + LS_ABOUT, + LS_CALENDAR_WEEK_SHORT, + LS_MONDAY_SHORT, + LS_TUESDAY_SHORT, + LS_WEDNESDAY_SHORT, + LS_THURSDAY_SHORT, + LS_FRIDAY_SHORT, + LS_SATURDAY_SHORT, + LS_SUNDAY_SHORT, + LS_TITLE_ENTER_PASSWORD, + LS_PASSWORD, + LS_CONFIRM_PASSWORD, + LS_OK, + LS_CANCEL, + LS_TITLE_OPEN_PASSWORD, + LS_CLOSE, + LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT, + LS_YES, + LS_NO, + LOCALIZED_STRING_COUNT +}; + +enum Language { + L_GERMAN, + L_ENGLISH, + L_FRENCH, + LANGUAGE_COUNT +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *localized_strings_storage[LANGUAGE_COUNT][LOCALIZED_STRING_COUNT]; + +#ifdef __cplusplus +} +#endif diff --git a/src/work-calendar.cpp b/src/work-calendar.cpp index 42097ae..3d2f962 100644 --- a/src/work-calendar.cpp +++ b/src/work-calendar.cpp @@ -5,10 +5,11 @@ #include #include +#include "localization.h" + #include "inter.h" #include "inter-bold.h" - extern bool should_exit; extern bool show_demo_window; @@ -37,7 +38,7 @@ int get_current_year(){ // tm_year is years since 1900 return year = current_year; -} +} bool is_leap_year(int year) { return ((year % 4) == 0 && (year % 100) != 0) || year % 400 == 0; @@ -77,18 +78,49 @@ static uint8_t derived_key[hydro_secretbox_KEYBYTES]; static char password_input_buffer[256]; static char password_confirmation_input_buffer[256]; +static Language current_language = L_GERMAN; +static Language selected_language = L_GERMAN; + +const char *get_localized_string(Localized_String localized_string) { + if(localized_strings_storage[current_language][localized_string]) { + return localized_strings_storage[current_language][localized_string]; + } + + return localized_strings_storage[L_ENGLISH][localized_string]; +} + void init() { hydro_init(); ImGuiIO &io = ImGui::GetIO(); inter_regular = io.Fonts->AddFontFromMemoryCompressedTTF(inter_compressed_data, inter_compressed_size); inter_bold = io.Fonts->AddFontFromMemoryCompressedTTF(inter_bold_compressed_data, inter_bold_compressed_size); year = get_current_year(); + + ImGuiSettingsHandler settings_handler = {}; + settings_handler.TypeName = "Calendar"; + settings_handler.TypeHash = ImHashStr("Calendar"); + settings_handler.ReadOpenFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, const char *name) -> void * { + return (void *)-1; + }; + settings_handler.ReadLineFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, void *entry, const char *line) { + if (strncmp(line, "language", sizeof("language") - 1) == 0) { + for (int i = 0; i < LANGUAGE_COUNT; i++) { + if (strncmp(line + sizeof("language"), localized_strings_storage[i][LS_LANGUAGE_NAME], strlen(localized_strings_storage[i][LS_LANGUAGE_NAME])) == 0) + selected_language = (Language)i; + } + } + }; + settings_handler.WriteAllFn = [](ImGuiContext *context, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buffer) { + buffer->append("[Calendar][Settings]\n"); + buffer->appendf("language=%s\n\n", get_localized_string(LS_LANGUAGE_NAME)); + }; + ImGui::AddSettingsHandler(&settings_handler); } static char *save_file_path = NULL; static const char *wiki_url = "https://gitea.ammerhai.com/Ammerhai/work-calendar/wiki"; -static const char *month_names[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}; +static Localized_String month_names[] = { LS_JANUARY, LS_FEBRUARY, LS_MARCH, LS_APRIL, LS_MAY, LS_JUNE, LS_JULY, LS_AUGUST, LS_SEPTEMBER, LS_OCTOBER, LS_NOVEMBER, LS_DECEMBER }; typedef struct Category { bool editing; @@ -222,6 +254,8 @@ void load(){ } void per_frame(){ + current_language = selected_language; + ImGuiStyle &style = ImGui::GetStyle(); ImVec2 center = ImGui::GetMainViewport()->GetCenter(); @@ -242,12 +276,12 @@ void per_frame(){ ImGui::DockSpaceOverViewport(main_viewport_dock, ImGui::GetMainViewport(), ImGuiDockNodeFlags_NoTabBar); ImGuiID close_popup = ImGui::GetID("Close"); - ImGuiID save_password_popup = ImGui::GetID("Passwort eingeben##Save"); - ImGuiID open_password_popup = ImGui::GetID("Passwort eingeben##Open"); + ImGuiID save_password_popup = ImGui::GetID("###Save"); + ImGuiID open_password_popup = ImGui::GetID("###Open"); if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("Datei")) { - if (ImGui::MenuItem("Öffnen", "Ctrl + O")) { + if (ImGui::BeginMenu(get_localized_string(LS_FILE))) { + if (ImGui::MenuItem(get_localized_string(LS_OPEN), "Ctrl + O")) { char *new_save_file_path = open_file_dialog(); if (new_save_file_path) { if (save_file_path) free(save_file_path); @@ -255,11 +289,11 @@ void per_frame(){ ImGui::OpenPopup(open_password_popup); } } - if (ImGui::MenuItem("Speichern", "Ctrl + S", false, !!save_file_path)) { + if (ImGui::MenuItem(get_localized_string(LS_SAVE), "Ctrl + S", false, !!save_file_path)) { save(); } - if (ImGui::MenuItem("Speichern unter", "Ctrl + Shift + S")) { + if (ImGui::MenuItem(get_localized_string(LS_SAVE_AS), "Ctrl + Shift + S")) { char *new_save_file_path = save_file_dialog(); if (new_save_file_path) { if (save_file_path) free(save_file_path); @@ -270,7 +304,7 @@ void per_frame(){ //TODO ImGui::Separator(); - if (ImGui::MenuItem("Kalender Schließen", "Ctrl + X")) { + if (ImGui::MenuItem(get_localized_string(LS_CLOSE_CALENDAR), "Ctrl + X")) { //TODO if calendar is opened warning if (save_file_path) free(save_file_path); @@ -284,24 +318,31 @@ void per_frame(){ } ImGui::Separator(); - if (ImGui::MenuItem("Beenden", NULL)) { + if (ImGui::MenuItem(get_localized_string(LS_EXIT), NULL)) { ImGui::OpenPopup(close_popup); } ImGui::EndMenu(); } - if (ImGui::BeginMenu("Ansicht")) { - ImGui::MenuItem("Legende", NULL, &legend_visible); + if (ImGui::BeginMenu(get_localized_string(LS_VIEW))) { + ImGui::MenuItem(get_localized_string(LS_LEGEND), NULL, &legend_visible); + if (ImGui::BeginMenu(get_localized_string(LS_LANGUAGE))) { + for (int language = 0; language < LANGUAGE_COUNT; language++) { + if (ImGui::MenuItem(localized_strings_storage[language][LS_LANGUAGE_NAME], NULL, current_language == language)) + selected_language = (Language) language; + } + ImGui::EndMenu(); + } ImGui::EndMenu(); } - if (ImGui::BeginMenu("Hilfe")) { - ImGui::MenuItem("Demo", NULL, &show_demo_window); - if (ImGui::MenuItem("Wiki", NULL)) { + if (ImGui::BeginMenu(get_localized_string(LS_HELP))) { + ImGui::MenuItem(get_localized_string(LS_DEMO), NULL, &show_demo_window); + if (ImGui::MenuItem(get_localized_string(LS_WIKI), NULL)) { ImGui::GetPlatformIO().Platform_OpenInShellFn(ImGui::GetCurrentContext(), wiki_url); } ImGui::Separator(); - ImGui::MenuItem("Über", NULL); //TODO + ImGui::MenuItem(get_localized_string(LS_ABOUT), NULL); //TODO ImGui::EndMenu(); } @@ -309,24 +350,24 @@ void per_frame(){ } ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("Passwort eingeben##Save", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::BeginPopupModal(get_localized_string(LS_TITLE_ENTER_PASSWORD), NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); ImGui::AlignTextToFramePadding(); - ImGui::Text("Passwort"); + ImGui::Text("%s", get_localized_string(LS_PASSWORD)); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); - ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password); + ImGui::InputText("##Password", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password); ImGui::AlignTextToFramePadding(); - ImGui::Text("Passwort bestätigen"); + ImGui::Text("%s", get_localized_string(LS_CONFIRM_PASSWORD)); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); - ImGui::InputText("##Passwort bestätigen", password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer), ImGuiInputTextFlags_Password); + ImGui::InputText("##Confirm_Password", password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer), ImGuiInputTextFlags_Password); ImGui::PopStyleVar(); - if (ImGui::Button("OK", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_OK), ImVec2(120, 0))) { if (hydro_compare((uint8_t *)password_input_buffer, (uint8_t *) password_confirmation_input_buffer, sizeof(password_input_buffer)) == 0) { hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT, master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS); @@ -339,7 +380,7 @@ void per_frame(){ } ImGui::SameLine(); - if (ImGui::Button("Abbrechen", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_CANCEL), ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); hydro_memzero(password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer)); @@ -349,18 +390,18 @@ void per_frame(){ } ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("Passwort eingeben##Open", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::BeginPopupModal(get_localized_string(LS_TITLE_OPEN_PASSWORD), NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); ImGui::AlignTextToFramePadding(); - ImGui::Text("Passwort"); + ImGui::Text("%s", get_localized_string(LS_PASSWORD)); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password); ImGui::PopStyleVar(); - if (ImGui::Button("OK", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_OK), ImVec2(120, 0))) { hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT, master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS); hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); @@ -370,28 +411,28 @@ void per_frame(){ } ImGui::SameLine(); - if (ImGui::Button("Abbrechen", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_CANCEL), ImVec2(120, 0))) { if (save_file_path) free(save_file_path); save_file_path = NULL; ImGui::CloseCurrentPopup(); hydro_memzero(password_input_buffer, sizeof(password_input_buffer)); } - ImGui::EndPopup(); + ImGui::EndPopup(); } ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("Close", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Möchten Sie wirklich beenden?"); + if (ImGui::BeginPopupModal(get_localized_string(LS_CLOSE), NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("%s", get_localized_string(LS_QUESTION_DO_YOU_REALLY_WANT_TO_QUIT)); ImGui::SetItemDefaultFocus(); - if (ImGui::Button("Ja", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_YES), ImVec2(120, 0))) { should_exit = true; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); - if (ImGui::Button("Nein", ImVec2(120, 0))) { + if (ImGui::Button(get_localized_string(LS_NO), ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } @@ -437,7 +478,7 @@ void per_frame(){ if (ImGui::BeginTable("Month", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::TextAligned(0.5, -FLT_MIN, month_names[month - 1]); + ImGui::TextAligned(0.5, -FLT_MIN, get_localized_string(month_names[month - 1])); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -446,7 +487,7 @@ void per_frame(){ if (ImGui::BeginTable("CalendarWeek", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::TextAligned(0.5, -FLT_MIN, "KW"); + ImGui::TextAligned(0.5, -FLT_MIN, get_localized_string(LS_CALENDAR_WEEK_SHORT)); for (int day = 1; day <= days_per_month(year, month); day++) { int weekday = weekday_from_day(year, month, day); @@ -474,13 +515,13 @@ void per_frame(){ if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){ - ImGui::TableSetupColumn("Mo"); - ImGui::TableSetupColumn("Di"); - ImGui::TableSetupColumn("Mi"); - ImGui::TableSetupColumn("Do"); - ImGui::TableSetupColumn("Fr"); - ImGui::TableSetupColumn("Sa"); - ImGui::TableSetupColumn("So"); + ImGui::TableSetupColumn(get_localized_string(LS_MONDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_TUESDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_WEDNESDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_THURSDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_FRIDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_SATURDAY_SHORT)); + ImGui::TableSetupColumn(get_localized_string(LS_SUNDAY_SHORT)); ImGui::TableHeadersRow(); ImGui::EndTable();