feat: Speiseplan-Inhalte variabel mehrsprachig (Teil 2)

Gerichtnamen, Essenszeiten und Allergen-/Zusatzstoff-Bezeichnungen sind
jetzt genauso dynamisch mehrsprachig wie die Bedienoberfläche aus
v1.10.0, statt fest auf Englisch/Polnisch begrenzt zu sein. Neue
Übersetzungstabellen ersetzen die alten name_en/name_pl-Spalten,
DeepL-Auto-Übersetzung und CSV-Import/-Export unterstützen jetzt
beliebig viele aktive Sprachen.
This commit is contained in:
2026-07-20 11:10:06 +02:00
parent 0d69d77d4e
commit cd8b0fab9f
24 changed files with 723 additions and 290 deletions
@@ -0,0 +1,71 @@
-- Teil 2 des "mehrsprachig variabel"-Umbaus: Speiseplan-Inhalte
-- (Gerichte, Essenszeiten, Allergene, Zusatzstoffe) bekommen eine
-- Übersetzungstabelle je Ressource statt fester name_en/name_pl-
-- Spalten (Teil 1, v1.10.0, hat dasselbe schon für die
-- Bedienoberfläche gemacht). Deutsch bleibt die Basis-Spalte
-- (name/label) auf der jeweiligen Haupttabelle.
--
-- WICHTIG bei Deployments mit echten Daten: vor dem Einspielen ein
-- Backup ziehen (siehe DEPLOYMENT.md, Abschnitt "Backup") – Schritt 3
-- entfernt die alten name_en/name_pl-Spalten unwiderruflich, nachdem
-- Schritt 2 ihren Inhalt in die neuen Tabellen übernommen hat.
-- ---------- Schritt 1: neue Tabellen ----------
create table dish_translations (
dish_id uuid not null references dishes(id) on delete cascade,
language_code text not null references languages(code) on delete cascade,
name text not null,
primary key (dish_id, language_code)
);
create table section_translations (
section_key text not null references sections(key) on delete cascade,
language_code text not null references languages(code) on delete cascade,
name text not null,
primary key (section_key, language_code)
);
-- allergens.code/additives.code sind vom Admin umbenennbar (Primary
-- Key), anders als dishes.id/sections.key – "on update cascade" nötig,
-- damit eine Umbenennung des Kürzels nicht die Übersetzungszeilen
-- verwaist.
create table allergen_translations (
allergen_code text not null references allergens(code) on delete cascade on update cascade,
language_code text not null references languages(code) on delete cascade,
name text not null,
primary key (allergen_code, language_code)
);
create table additive_translations (
additive_code text not null references additives(code) on delete cascade on update cascade,
language_code text not null references languages(code) on delete cascade,
name text not null,
primary key (additive_code, language_code)
);
-- ---------- Schritt 2: bestehende Übersetzungen übernehmen ----------
insert into dish_translations (dish_id, language_code, name)
select id, 'en', name_en from dishes where name_en is not null and trim(name_en) != '';
insert into dish_translations (dish_id, language_code, name)
select id, 'pl', name_pl from dishes where name_pl is not null and trim(name_pl) != '';
insert into section_translations (section_key, language_code, name)
select key, 'en', label_en from sections where label_en is not null and trim(label_en) != '';
insert into section_translations (section_key, language_code, name)
select key, 'pl', label_pl from sections where label_pl is not null and trim(label_pl) != '';
insert into allergen_translations (allergen_code, language_code, name)
select code, 'en', name_en from allergens where name_en is not null and trim(name_en) != '';
insert into allergen_translations (allergen_code, language_code, name)
select code, 'pl', name_pl from allergens where name_pl is not null and trim(name_pl) != '';
insert into additive_translations (additive_code, language_code, name)
select code, 'en', name_en from additives where name_en is not null and trim(name_en) != '';
insert into additive_translations (additive_code, language_code, name)
select code, 'pl', name_pl from additives where name_pl is not null and trim(name_pl) != '';
-- ---------- Schritt 3: alte Spalten entfernen ----------
alter table dishes drop column name_en, drop column name_pl;
alter table sections drop column label_en, drop column label_pl;
alter table allergens drop column name_en, drop column name_pl;
alter table additives drop column name_en, drop column name_pl;
+71 -32
View File
@@ -16,42 +16,81 @@ delete from allergens;
delete from additives;
-- ---------- Essenszeiten ----------
insert into sections (key, label, label_en, label_pl, time_range, sort) values
('brotzeit', 'Brotzeit', 'Breakfast', 'Śniadanie', '09:00 - 10:00', 1),
('mittagessen', 'Mittagessen', 'Lunch', 'Obiad', '11:30 - 13:30', 2),
('mittagessen_veg', 'Mittagessen vegetarisch', 'Lunch (vegetarian)', 'Obiad (wegetariański)', '11:30 - 13:30', 3),
('suppe', 'Suppe', 'Soup', 'Zupa', '11:30 - 13:30', 4);
insert into sections (key, label, time_range, sort) values
('brotzeit', 'Brotzeit', '09:00 - 10:00', 1),
('mittagessen', 'Mittagessen', '11:30 - 13:30', 2),
('mittagessen_veg', 'Mittagessen vegetarisch', '11:30 - 13:30', 3),
('suppe', 'Suppe', '11:30 - 13:30', 4);
insert into section_translations (section_key, language_code, name) values
('brotzeit', 'en', 'Breakfast'),
('brotzeit', 'pl', 'Śniadanie'),
('mittagessen', 'en', 'Lunch'),
('mittagessen', 'pl', 'Obiad'),
('mittagessen_veg', 'en', 'Lunch (vegetarian)'),
('mittagessen_veg', 'pl', 'Obiad (wegetariański)'),
('suppe', 'en', 'Soup'),
('suppe', 'pl', 'Zupa');
-- ---------- Allergene (EU-Kennzeichnung, Buchstaben) ----------
insert into allergens (code, name, name_en, name_pl, sort) values
('A', 'Glutenhaltiges Getreide', 'Cereals containing gluten', 'Zboża zawierające gluten', 1),
('B', 'Krebstiere', 'Crustaceans', 'Skorupiaki', 2),
('C', 'Eier', 'Eggs', 'Jaja', 3),
('D', 'Fisch', 'Fish', 'Ryby', 4),
('E', 'Erdnüsse', 'Peanuts', 'Orzeszki ziemne', 5),
('F', 'Sojabohnen', 'Soybeans', 'Soja', 6),
('G', 'Milch (einschließlich Laktose)', 'Milk (including lactose)', 'Mleko (w tym laktoza)', 7),
('H', 'Schalenfrüchte (Nüsse)', 'Tree nuts', 'Orzechy', 8),
('I', 'Sellerie', 'Celery', 'Seler', 9),
('J', 'Senf', 'Mustard', 'Gorczyca', 10),
('K', 'Sesamsamen', 'Sesame seeds', 'Nasiona sezamu', 11),
('L', 'Schwefeldioxid und Sulfite', 'Sulphur dioxide and sulphites', 'Dwutlenek siarki i siarczyny', 12),
('M', 'Lupinen', 'Lupin', 'Łubin', 13),
('N', 'Weichtiere', 'Molluscs', 'Mięczaki', 14);
insert into allergens (code, name, sort) values
('A', 'Glutenhaltiges Getreide', 1),
('B', 'Krebstiere', 2),
('C', 'Eier', 3),
('D', 'Fisch', 4),
('E', 'Erdnüsse', 5),
('F', 'Sojabohnen', 6),
('G', 'Milch (einschließlich Laktose)', 7),
('H', 'Schalenfrüchte (Nüsse)', 8),
('I', 'Sellerie', 9),
('J', 'Senf', 10),
('K', 'Sesamsamen', 11),
('L', 'Schwefeldioxid und Sulfite', 12),
('M', 'Lupinen', 13),
('N', 'Weichtiere', 14);
insert into allergen_translations (allergen_code, language_code, name) values
('A', 'en', 'Cereals containing gluten'), ('A', 'pl', 'Zboża zawierające gluten'),
('B', 'en', 'Crustaceans'), ('B', 'pl', 'Skorupiaki'),
('C', 'en', 'Eggs'), ('C', 'pl', 'Jaja'),
('D', 'en', 'Fish'), ('D', 'pl', 'Ryby'),
('E', 'en', 'Peanuts'), ('E', 'pl', 'Orzeszki ziemne'),
('F', 'en', 'Soybeans'), ('F', 'pl', 'Soja'),
('G', 'en', 'Milk (including lactose)'), ('G', 'pl', 'Mleko (w tym laktoza)'),
('H', 'en', 'Tree nuts'), ('H', 'pl', 'Orzechy'),
('I', 'en', 'Celery'), ('I', 'pl', 'Seler'),
('J', 'en', 'Mustard'), ('J', 'pl', 'Gorczyca'),
('K', 'en', 'Sesame seeds'), ('K', 'pl', 'Nasiona sezamu'),
('L', 'en', 'Sulphur dioxide and sulphites'), ('L', 'pl', 'Dwutlenek siarki i siarczyny'),
('M', 'en', 'Lupin'), ('M', 'pl', 'Łubin'),
('N', 'en', 'Molluscs'), ('N', 'pl', 'Mięczaki');
-- ---------- Zusatzstoffe (Ziffern) ----------
insert into additives (code, name, name_en, name_pl, sort) values
('1', 'mit Farbstoff', 'with colouring', 'z barwnikiem', 1),
('2', 'mit Konservierungsstoff', 'with preservative', 'z konserwantem', 2),
('3', 'mit Antioxidationsmittel', 'with antioxidant', 'z przeciwutleniaczem', 3),
('4', 'mit Geschmacksverstärker', 'with flavour enhancer', 'ze wzmacniaczem smaku', 4),
('5', 'geschwefelt', 'sulphured', 'siarkowany', 5),
('6', 'geschwärzt', 'blackened', 'barwiony na czarno', 6),
('7', 'gewachst', 'waxed', 'woskowany', 7),
('8', 'mit Phosphat', 'with phosphate', 'z fosforanem', 8),
('9', 'mit Süßungsmittel(n)', 'with sweetener(s)', 'ze słodzikiem/słodzikami', 9),
('10', 'enthält eine Phenylalaninquelle', 'contains a source of phenylalanine', 'zawiera źródło fenyloalaniny', 10),
('11', 'koffeinhaltig', 'caffeinated', 'zawiera kofeinę', 11);
insert into additives (code, name, sort) values
('1', 'mit Farbstoff', 1),
('2', 'mit Konservierungsstoff', 2),
('3', 'mit Antioxidationsmittel', 3),
('4', 'mit Geschmacksverstärker', 4),
('5', 'geschwefelt', 5),
('6', 'geschwärzt', 6),
('7', 'gewachst', 7),
('8', 'mit Phosphat', 8),
('9', 'mit Süßungsmittel(n)', 9),
('10', 'enthält eine Phenylalaninquelle', 10),
('11', 'koffeinhaltig', 11);
insert into additive_translations (additive_code, language_code, name) values
('1', 'en', 'with colouring'), ('1', 'pl', 'z barwnikiem'),
('2', 'en', 'with preservative'), ('2', 'pl', 'z konserwantem'),
('3', 'en', 'with antioxidant'), ('3', 'pl', 'z przeciwutleniaczem'),
('4', 'en', 'with flavour enhancer'), ('4', 'pl', 'ze wzmacniaczem smaku'),
('5', 'en', 'sulphured'), ('5', 'pl', 'siarkowany'),
('6', 'en', 'blackened'), ('6', 'pl', 'barwiony na czarno'),
('7', 'en', 'waxed'), ('7', 'pl', 'woskowany'),
('8', 'en', 'with phosphate'), ('8', 'pl', 'z fosforanem'),
('9', 'en', 'with sweetener(s)'), ('9', 'pl', 'ze słodzikiem/słodzikami'),
('10', 'en', 'contains a source of phenylalanine'), ('10', 'pl', 'zawiera źródło fenyloalaniny'),
('11', 'en', 'caffeinated'), ('11', 'pl', 'zawiera kofeinę');
-- ---------- Testgerichte ----------
insert into dishes (name, price) values