News:

SMF - Just Installed!

Main Menu

Recent posts

#11
Announcements & Releases / 1.1.22
Last post by hitsumo - Aug 09, 2026, 05:31 AM
# Anime Tracker 1.1.22

**Release date:** 2026-07-25

## New: AniList import now produces tagged names

- **Animes imported from AniList no longer arrive with a single name.**
  Until now the import kept only the Romaji name and threw away the English
  and native names AniList already provides. Those names are now written
  into the alternative-titles list with language tags:
 
  ```
  [en]Frieren: Beyond Journey's End|[ja]葬送のフリーレン
  ```
 
- **The visible payoff:** the Title Language preference (1.1.21) now works
  for imported animes too. A user whose preference is English or Japanese
  sees an imported anime in that language — previously such animes rendered
  as Romaji for everyone.
 
- **The native name's language is derived from the country.** AniList never
  states the native title's language, only the country of origin; the bridge
  is: Japan → Japanese, China/Taiwan → Chinese, Korea → Korean. For a country
  we cannot bridge, the name is stored **untagged** — a guessed tag would
  show the wrong title to users of that language, while an untagged name is
  honest.
 
- **Where it lands:** in online mode, on the suggestion record — when a
  moderator approves it, the tagged names travel into the catalog with the
  anime (the approval page already carried this field). In self-host, the
  locally added anime is born with the tags directly.
 

## Fixed: List Settings rendered unstyled on slow connections

- **The page-specific styles (tab bar, blue buttons, white section cards)
  sat at the very end of the page.** Every other page in the app carries
  these styles inside `<head>`; List Settings was the lone exception, with
  its style block just above `</body>`. On a slow or interrupted load the
  browser painted the content **unstyled** until the page's final bytes
  arrived — tabs showed as bare buttons, buttons as default grey (the
  global stylesheet still applied, so the header card looked normal and
  the breakage hit only this page's own parts).
- The style block moved into `<head>`, where every other page keeps it.
  Content and rule order are unchanged; the browser is now guaranteed to
  receive the styles **before** the content they style.

## Why MAL is unchanged

- **MAL's XML export carries a single name** (`series_title`); the file has
  no second field for an English or Japanese title. With no tag to derive,
  the MAL import path stays as it was.

## How it works (technical)

- The AniList query now fetches `title { romaji english native }` (`native`
  is new). The main-title choice is unchanged: Romaji, falling back to
  English.
- The English name is skipped when it is the same as the main title or just
  a case variant of it (AniList repeats names like "One Piece" in both
  fields). The same dedup applies to the native name.
- Tagged text goes through the same hygiene the add/edit form uses
  (`build_alt_titles`): pipes become spaces, a hand-typed tag at the front
  of a name is stripped, an unknown language code degrades to untagged. A
  row written by the import is as well-formed as one typed into the form.
- **Animes already in the catalog (matched entries) are untouched** — the
  import does not backfill missing tags; catalog content is curator
  territory.
- An older session draft (previewed on 1.1.21, committed on 1.1.22) lacks
  the new field; it silently stays empty — the same defensive pattern the
  country field used in 1.1.17.

## Schema / migration

- **No schema change.** The `alternative_titles` columns already existed;
  the only change is that the import writer now fills them.
  `migration/1.1.22/upgrade.sql` is a version stamp only.
- **No manual work on the central catalog server.** The tagged text rides
  inside the existing column, over the existing catalog wire.

## Changed / new files

- files/functions/anilist_import_helpers.php (native added to the query; anilist_native_lang + anilist_alt_titles are new)
- files/list_settings.php (both AniList commit write paths carry alternative_titles; page styles moved into head)
- files/migration/1.1.22/upgrade.sql (new, version stamp only)
- files/version.txt
#12
Duyurular ve Sürümler / 1.1.24
Last post by hitsumo - Aug 09, 2026, 05:30 AM
# Anime Tracker 1.1.24

**Yayın tarihi:** 2026-07-30

## Düzeltildi: yükseltmeden sonra bozuk görünen sayfalar

- **Sürüm yükseltmesinden sonra sayfa düzeninin bozulması sona erdi.** Stil
  ve betik dosyaları bugüne kadar hiç değişmeyen adreslerle linkleniyordu
  (`style.css`, `js/select_enhance.js`). Adres aynı kaldığı için tarayıcı,
  yükseltmeden sonra bile **eski** dosyayı kendi önbelleğinden servis etmeye
  devam ediyordu: yeni HTML, eski CSS ile buluşuyordu. 1.1.20 dağıtımının
  hemen ardından görülen bozuk görünümün sebebi tam olarak buydu.
- **Artık her yerel stil ve betik adresinin sonunda sürüm damgası var**
  (`css/base.css?v=1.1.24`). Sürüm değiştiği anda adres de değiştiği için
  tarayıcı dosyayı yeniden indirmek zorunda kalır. Yükseltmeden sonra
  Ctrl+F5 yapmaya, önbelleği elle temizlemeye gerek yok: sayfanın kendisi
  önbelleklenmediği için yeni HTML ilk açılışta gelir, damgalı adresleri
  görür ve taze CSS'i çeker.
- **Bu değişiklik sayfaları yavaşlatmaz.** Aynı sürüm boyunca adresler
  sabittir, yani dosyalar eskiden olduğu gibi süresiz önbelleklenmeye devam
  eder — yalnızca sürüm atladığında tazelenirler.
- **Stil dosyaları tek tek linklenir hâle geldi.** `style.css` yalnızca bir
  yükleyicidir; gerçek kurallar `css/` altındaki sekiz modüldedir ve
  `@import` ile çekilir. Sadece yükleyiciyi damgalamak işe yaramazdı:
  tarayıcı `style.css`'i yeniden okur, içindeki damgasız `@import`
  adreslerini görür ve `css/components.css`'i yine önbellekten verirdi.
  Sayfalar artık modüllerin her biri için damgalı bir link yazıyor —
  yükleyicinin `@import` sırası birebir korunarak, yani görsel sonuç aynı.

## Düzeltildi: yardım sayfasında var olmayan bir stil dosyası isteniyordu

- Yardım → Saat Dilimi alt sayfası, 0.6.7'deki dosya ayrışmasından beri var
  olmayan `help.css` dosyasını linklemeye devam ediyordu; istek her açılışta
  404 dönüyordu. Link kaldırıldı. Sayfanın görünümü değişmez — o stiller
  zaten `css/help.css` modülünden geliyordu.

## Nasıl çalışıyor (teknik)

- Yeni `files/functions/asset_helpers.php`: `asset_version()` damgayı
  `files/version.txt` içinden okur (`settings.version` satırından **değil** —
  damgalanan şeyler dosya olduğu için dosya sürümüyle eşleşmeleri gerekir;
  ayrıca kurulum sayfaları veritabanı yokken de çalışır). Değer URL'ye
  gireceği için doğrulanır; beklenmeyen bir içerik "sürüm yok" sayılır.
- `asset_styles()` bir sayfanın bütün stil linklerini üretir, `asset_url()`
  tek bir varlığın adresini kurar. İkisi de sayfanın `files/` köküne olan
  yolunu parametre alır: kökteki sayfalar için boş, `admin/` ve `help/`
  altındakiler için `../`.
- **Modül listesi `style.css`'in `@import` satırlarından çalışma anında
  okunur.** Böylece ikinci bir liste tutulmaz: yeni bir modül eklemek için
  yine yalnızca `style.css`'e `@import` yazmak yeterli. `style.css` kendi
  başına çalışan bir stil dosyası olarak da duruyor.
- Damgasız kalma ihtimalleri kapatıldı: `version.txt` okunamazsa dosyanın
  kendi değişiklik zamanı damga olarak kullanılır (geliştirme kopyasında da
  taze dosya gelir), `style.css` okunamazsa sayfa eski davranışa düşer ve
  yükleyiciyi linkler — hiçbir durumda stilsiz sayfa çıkmaz.
- Yardımcı, `functions.php` yükleyicisine eklendi; onu yüklemeyen üç sayfa
  (`setup`, `install`, `ai_notice` ve İngilizce eşleri) yardımcıyı doğrudan
  çağırır. Dosya bilerek kendi kendine yeter: veritabanı, dil dosyası ya da
  yapılandırma gerektirmez, bu yüzden uygulama kurulmadan önce de çalışır.

## Şema / migration

- **Şema değişikliği yok.** Değişiklik tamamen gösterim katmanındadır: ne
  yeni tablo/kolon ne yeni tercih var. `migration/1.1.24/upgrade.sql`
  yalnızca sürüm damgası taşır.
- **Merkez katalog sunucusunda elle işlem GEREKMEZ** — katalog teline
  dokunulmadı.

## Değişen / yeni dosyalar

- files/functions/asset_helpers.php (**yeni** — damga, varlık adresleri,
  modül listesi)
- files/functions.php (yardımcıyı yükleyiciye ekler)
- files/style.css (yalnızca yorum: `@import` listesinin artık modül
  listesi olarak da okunduğu not edildi)
- files/help/help_timezone.php (404 dönen `../help.css` linki kaldırıldı)
- Baş kısmında stil/betik linki olan bütün sayfalar (41 dosya):
  about, account, add_anime, ai_notice, ai_notice_en, anime_details,
  chronology, edit_anime, filler_edit, help, index, install, install_en,
  list_settings, login, logout, manage_genres, manage_tags, pending,
  recent, recommendations, register, request_invite, series_timeline,
  setup, setup_en, statistics; admin/ altında admin, admin_capabilities,
  admin_catalog_requests, admin_invites, admin_pending, admin_suggestions,
  admin_sync_example, admin_users; help/ altında help_basics,
  help_discovery, help_fields, help_series, help_sync, help_timezone
- files/migration/1.1.24/upgrade.sql (yeni, yalnızca sürüm damgası)
- files/version.txt

## Dağıtım notu

- `files/functions/asset_helpers.php` **yeni** bir dosyadır ve
  `functions.php` üzerinden her sayfada yüklenir. Sunucuya yüklenmezse
  sayfalar ilk yardımcı çağrısında ölür, o yüzden bu sürümde dosya
  yüklemesinin eksiksiz olduğu doğrulanmalı.
- `css/` ve `js/` klasörlerinin içeriği değişmedi; damga adreste taşındığı
  için o dosyalara dokunmak gerekmez.
#13
Duyurular ve Sürümler / 1.1.23
Last post by hitsumo - Aug 09, 2026, 05:29 AM
# Anime Tracker 1.1.23

**Yayın tarihi:** 2026-07-26

## Yeni: Seri Kronolojisi'ne "Yayın Tarihi" sekmesi

- **Seri kronolojisi sayfası artık iki sekmeli.** "Zincir Sırası" bugüne
  kadarki görünümün kendisi: Sıradaki Anime bağlantılarını halka halka izler.
  Yeni "Yayın Tarihi" sekmesi ise aynı seri adını taşıyan **bütün** animeleri
  ilk gösterim tarihine göre dizer — TV dizilerinde başlangıç–bitiş aralığı
  (gün.ay.yıl), film ve özel bölümlerde tek gösterim tarihi görünür.
- **Neden?** Zincir görünümü elle kurulan bağlantılara muhtaçtır: tek bir
  eksik halka listeyi ikiye böler ve katalogdan içe aktarılan anime hep
  bağlantısız doğar. Üstelik doğrusal bir zincir, iç içe geçen yayın
  dönemlerini gösteremez — bir dizinin yayını sürerken çıkan filmler bunun
  tipik örneğidir. Yayın Tarihi sekmesi zincire hiç bakmadığı için bu
  sorunların ikisinden de etkilenmez: eksik halka onu bölemez, tarih
  aralıkları çakışmaları olduğu gibi gösterir.
- **Tarihi girilmemiş kayıtlar** listenin sonuna düşer ve "tarih yok"
  etiketiyle görünür — serinin başlangıcıymış gibi öne geçmezler.
- **Sekme seçimi oturum boyunca hatırlanır:** bir seride Yayın Tarihi'ne
  geçtiyseniz başka bir serinin kronolojisi de o sekmeyle açılır; kalıcı
  varsayılanınız değişmez.

## Yeni: Liste Ayarları'nda kalıcı varsayılan

- Liste Ayarları → Genel Ayarlar'a "Seri Kronolojisi Görünümü" tercihi
  eklendi: sayfa varsayılan olarak hangi sekmeyle açılsın — Zincir Sırası
  (varsayılan, eski davranış) ya da Yayın Tarihi. Kronoloji Görünümü
  (1.1.15) tercihinin birebir kalıbı: sayfadaki sekmeler bu varsayılanı
  ezmeden geçici geçiş yapar. Bu tercih yalnızca sizi etkiler.

## Yeni: Buton artık seri adından da açılıyor

- Detay sayfasındaki "Seri Kronolojisi" butonu eskiden yalnızca zincire
  girmiş (Sıradaki Anime bağlantısı olan) animelerde çıkıyordu. Artık
  **zincire hiç girmemiş ama aynı seri adını taşıyan başka kayıtları olan**
  animede de çıkar — katalogdan içe aktarılıp henüz bağlanmamış bir anime,
  Yayın Tarihi sekmesi sayesinde serisinin çizelgesine hemen girer.

## Düzeltildi: +18 başlıklar seri kronolojisinde maskesizdi

- +18 içerik tercihi kapalı bir kullanıcı, zincirde +18 damgalı bir üye
  varsa başlığını seri kronolojisinde açık açık görüyordu — uygulamanın
  diğer ilişkili-anime kartları (1.1.2'den beri) bu başlığı nötr yer
  tutucuyla maskelerken bu sayfa istisna kalmıştı. Artık iki sekmede de
  aynı maske uygulanır: kart kalır, başlık sızmaz; tercihi açık kullanıcı
  gerçek başlığı görmeye devam eder.

## Nasıl çalışıyor (teknik)

- Yayın Tarihi sekmesi `series_name` üzerinden tek sorguyla beslenir;
  sıralama `release_date` (NULL'lar sona). Zincir yürüyüşü, kart şablonu ve
  ilerleme/durum rozetleri iki sekmede ortaktır.
- Sekme seçimi 1.1.15'teki kronoloji modu kalıbını izler: oturumdaki geçici
  seçim > kayıtlı kişisel varsayılan > Zincir Sırası. Kalıcı tercih
  `user_pref` tablosuna `series_timeline_mode` adıyla yazılır; sekmeler
  sayfa içi bağlantıdır, kalıcı kayıt yalnızca Liste Ayarları'ndan yapılır.
- Seri adı boş animede Yayın Tarihi sekmesi gösterilmez; sayfa eski
  davranışıyla yalnızca zinciri çizer.

## Şema / migration

- **Şema değişikliği yok.** Yeni tercih, var olan `user_pref` anahtar-değer
  tablosuna satır olarak girer; kullanılan tüm kolonlar zaten mevcuttu.
  `migration/1.1.23/upgrade.sql` yalnızca sürüm damgası taşır.
- **Merkez katalog sunucusunda elle işlem GEREKMEZ** — değişiklik tamamen
  gösterim katmanındadır.

## Değişen / yeni dosyalar

- files/series_timeline.php (sekmeler, yayın tarihi görünümü, +18 maskesi)
- files/anime_details.php (Seri Kronolojisi butonu seri adından da açılır)
- files/list_settings.php (Seri Kronolojisi Görünümü tercihi)
- files/set_series_timeline_mode.php (yeni, kalıcı tercihi yazan uç nokta)
- files/functions/series_helpers.php (mod çözümü + yayın tarihi sorgusu)
- files/lang/tr.php, files/lang/en.php (yeni metinler)
- files/migration/1.1.23/upgrade.sql (yeni, yalnızca sürüm damgası)
- files/version.txt
#14
Duyurular ve Sürümler / 1.1.22
Last post by hitsumo - Aug 09, 2026, 05:28 AM
# Anime Tracker 1.1.22

**Yayın tarihi:** 2026-07-25

## Yeni: AniList içe aktarma artık etiketli isim üretiyor

- **AniList'ten içe aktarılan animeler artık tek isimle gelmiyor.** Şimdiye
  kadar içe aktarma her animenin yalnızca Romaji adını tutuyor, AniList'in
  verdiği İngilizce ve orijinal (native) adları çöpe atıyordu. Artık o adlar
  dil etiketiyle alternatif isim listesine yazılıyor:
 
  ```
  [en]Frieren: Beyond Journey's End|[ja]葬送のフリーレン
  ```
 
- **Bunun görünür sonucu:** Başlık Dili tercihi (1.1.21) içe aktarılan
  animelerde de çalışıyor. Tercihi İngilizce ya da Japonca olan bir kullanıcı,
  içe aktarmayla kataloğa giren bir animeyi artık o dilde görür — eskiden bu
  animeler herkes için Romaji'de kalıyordu.
 
- **Orijinal adın dili ülkeden bulunur.** AniList native adın dilini
  söylemez, yalnızca yapım ülkesini verir; köprü şöyle kurulur: Japonya →
  Japonca, Çin/Tayvan → Çince, Kore → Korece. Köprülenemeyen bir ülkede isim
  **etiketsiz** yazılır — yanlış tahmin edilmiş bir etiket, o dili seçen
  kullanıcıya yanlış başlık gösterirdi; etiketsiz isim ise dürüsttür.
 
- **Nereye yazılır?** Online modda öneri kaydına: moderatör onayladığında
  etiketli isimler animeyle birlikte kataloğa geçer (onay sayfası bu alanı
  zaten taşıyordu). Self-host'ta yerel eklenen anime doğrudan etiketli doğar.
 

## Düzeltildi: Liste Ayarları yavaş bağlantıda stilsiz görünüyordu

- **Sayfaya özgü stiller (sekme çubuğu, mavi butonlar, beyaz bölüm kartları)
  sayfanın en sonunda duruyordu.** Uygulamadaki diğer bütün sayfalar bu
  stilleri `<head>` içinde taşırken, Liste Ayarları tek istisnaydı: stil
  bloğu `</body>`'nin hemen üstündeydi. Yavaş ya da yarıda kesilen bir
  yüklemede tarayıcı, sayfanın son baytları gelene kadar içeriği **stilsiz**
  çiziyordu — sekmeler çıplak düğme, butonlar varsayılan gri görünüyordu
  (genel stiller dosyadan geldiği için sayfanın üst kartı normal kalıyor,
  bozulma yalnızca bu sayfaya özgü parçalarda oluyordu).
- Stil bloğu diğer sayfalardaki yerine, `<head>` içine taşındı. İçerik ve
  kural sırası değişmedi; yalnızca tarayıcının stilleri **içerikten önce**
  alması garanti oldu.

## MAL neden değişmedi

- **MAL'ın XML dışa aktarımı tek bir isim taşır** (`series_title`); dosyada
  İngilizce ya da Japonca ad diye ikinci bir alan yoktur. Üretilebilecek
  etiket olmadığı için MAL içe aktarma yolu olduğu gibi kaldı.

## Nasıl çalışıyor (teknik)

- AniList sorgusu artık `title { romaji english native }` çekiyor (`native`
  yeni). Ana başlık seçimi değişmedi: Romaji, yoksa İngilizce.
- İngilizce ad, ana başlığın aynısı ya da yalnızca büyük/küçük harf farklı
  kopyasıysa yazılmaz (AniList "One Piece" gibi adları iki alanda da verir).
  Aynı eleme native ad için de geçerlidir.
- Etiketli metin, ekleme/düzenleme formunun kullandığı aynı süzgeçten
  (`build_alt_titles`) geçer: boru işareti boşluğa çevrilir, ismin başındaki
  elle yazılmış bir etiket ayıklanır, bilinmeyen dil kodu etiketsize düşer.
  Yani içe aktarmanın yazdığı satır, formda yazılmış gibi kurallıdır.
- **Katalogda zaten olan (eşleşen) animelere dokunulmaz** — içe aktarma
  eksik etiketleri geriye dönük tamamlamaz; katalog içeriği küratör alanıdır.
- Eski bir oturum taslağı (önizleme 1.1.21'de, uygulama 1.1.22'de) yeni alanı
  taşımaz; alan sessizce boş kalır — ülke alanının 1.1.17'deki savunmasıyla
  aynı kalıp.

## Şema / migration

- **Şema değişikliği yok.** `alternative_titles` kolonları zaten vardı;
  değişen tek şey içe aktarma yazıcısının onları doldurmaya başlaması.
  `migration/1.1.22/upgrade.sql` yalnızca sürüm damgası taşır.
- **Merkez katalog sunucusunda elle işlem GEREKMEZ.** Etiketli metin mevcut
  kolonun içinde, mevcut katalog teliyle akar.

## Değişen / yeni dosyalar

- files/functions/anilist_import_helpers.php (sorguya native eklendi; anilist_native_lang + anilist_alt_titles yeni)
- files/list_settings.php (AniList commit'in iki yazma yolu alternative_titles taşıyor; sayfa stilleri head'e taşındı)
- files/migration/1.1.22/upgrade.sql (yeni, yalnızca sürüm damgası)
- files/version.txt
#15
Duyurular ve Sürümler / v1.1.21
Last post by hitsumo - Jul 25, 2026, 02:21 PM
# Anime Tracker 1.1.21

**Yayın tarihi:** 2026-07-24

## Yeni: Başlık Dili — artık sadece İngilizce değil

- **Liste Ayarları'ndaki "İngilizce başlıkları göster" onay kutusu, gerçek bir
  dil seçimine dönüştü.** Anime başlıklarını hangi dilde görmek istediğinizi
  seçersiniz: **Romaji** (varsayılan), İngilizce, Japonca, Türkçe, Çince,
  Korece, Fransızca.
- **Seçtiğiniz dilde başlığı olmayan anime Romaji'de kalır.** Yani katalogda
  henüz olmayan bir dili seçmek zararsızdır — hiçbir yer boş görünmez.
- **Başlıkların dili nereden geliyor?** 1.1.20'de eklediğimiz yerden: anime
  ekleme/düzenleme formunda alternatif isimlerin yanındaki dil kutusundan.
  Bir animeye Japonca isim ekleyip "Japonca" işaretlerseniz, Başlık Dili'ni
  Japonca yapan herkes onu görür.
- Tercih size özeldir ve **arayüz dilinden bağımsızdır** — arayüzü Türkçe
  kullanıp başlıkları Japonca görebilirsiniz.

## Düzeltildi: İstatistik sayfası başlık dilini yok sayıyordu

- **"Son İzlenenler" tablosu, tercihiniz ne olursa olsun başlıkları hep Romaji
  gösteriyordu.** 1.1.18'de bu hücre başlık-dili desteği alacak şekilde
  düzenlenmişti, ama sayfanın tercihi okuması için gereken çağrı eklenmemişti;
  sonuç olarak tablo sessizce varsayılana düşüyordu. Artık doğru çalışıyor.

## Kaldırıldı: title_english kolonu

- **1.1.20'de "geçici olarak duruyor" dediğimiz kolon emekli edildi.** Artık
  bir başlığın dili tek bir yerde yaşıyor: etiketli alternatif isim listesinde.
- **Neden duruyordu, neden şimdi gitti?** 1.1.20'de kolon, kaydetme anında
  `[en]` etiketinden türetilen bir "gösterim kısayolu"ydu; sayesinde o sürüm
  başlık basan hiçbir sayfaya dokunmak zorunda kalmadı. Ama o kolon yalnızca
  İngilizceyi anlatabildiği için Türkçe/Japonca başlıkların **saklanmasına**
  izin veriyor, **gösterilmesine** izin vermiyordu. Bu sürümde gösterim
  doğrudan etiketleri okuduğu için kolon gereksizleşti.
- **Veri kaybı yoktur.** İngilizce isimler zaten `[en]` etiketiyle alternatif
  isimler içinde duruyor; migration, kolonu düşürmeden önce gözden kaçmış
  olabilecek satırları da etiketler.

## Nasıl çalışıyor (teknik)

- Tercih artık bir dil kodu tutuyor (`display_title_lang`); eskiden bir
  aç/kapa değeriydi (`display_title_english`). Migration, "açık" olan
  kullanıcıları İngilizce'ye taşır.
- `display_title()` seçili dilin etiketini arar, bulamazsa Romaji başlığa
  düşer. Varsayılan Romaji tercihinde etiketli liste **hiç ayrıştırılmaz**,
  yani en sık karşılaşılan durumda ek bir maliyet yoktur.
- Başlık basan bütün sorgular artık `title_english` yerine
  `alternative_titles` çekiyor: anime detayı, istatistik, seri zaman çizelgesi,
  kronoloji, onay bekleyenler, konu içi anime linkleri.
- +18 gizleme güçlendi: eskiden yalnızca İngilizce başlık temizleniyordu, artık
  etiketli listenin tamamı temizleniyor — aksi halde adı gizlenen bir anime,
  başka bir dil seçili kullanıcıya adını sızdırabilirdi.

## Şema / migration

- `migration/1.1.21/upgrade.sql` **kolonu düşürür** (`animes` ve
  `catalog_requests`). Düşürmeden önce iki kurtarma adımı çalışır: İngilizce
  isim listede etiketsiz duruyorsa yerinde etiketlenir, hiç yoksa `[en]` olarak
  eklenir. Bu adım gereklidir çünkü 1.1.20'den sonra yapılan bir katalog
  senkronu etiketleri sunucudaki eski sürümle ezmiş olabilir.
- Tercih göçü aynı dosyada: `display_title_english = '1'` olan kullanıcılar
  `display_title_lang = 'en'` olur, eski satırlar silinir.
- **Migration yeniden çalıştırılabilir.** İlk iş olarak kolonu geri ekler
  (zaten varsa hata yok sayılır). Böylece yarım kalmış bir yükseltme ya da
  kolonun elle düşürülmüş olması yükseltmeyi kilitlemez — kurtarma adımları
  okuyacak bir kolon bulur, sonda yine düşürülür, sonuç her iki durumda aynı.

## Merkez katalog sunucusunda elle işlem GEREKİR

1.1.20 elle işlem gerektirmeyen istisnaydı; bu sürüm yeniden gerektiriyor.
**Sıra kritiktir:**

1. `catalog_server/` yeni sürümünü dağıtın (`catalog.php` + `admin_push.php`).
2. Sunucu veritabanında: `ALTER TABLE animes DROP COLUMN title_english;`
3. Uygulamayı dağıtın (migration kendiliğinden çalışır).
4. Tam katalog push.

Ters sırada (önce ALTER) eski `catalog.php` düşmüş bir kolonu okumaya çalışır
ve **bütün istemciler için senkron patlar**. Sunucuda `catalog_requests`
tablosu yoktur; orada yalnızca yukarıdaki tek `ALTER` çalıştırılır.

**Geride kalan kurulumlar:** 1.1.20 ve öncesinde kalan bir istemci katalogtan
artık İngilizce başlık alamaz ve o kurulumda başlıklar Romaji'ye düşer. Veri
kaybı değildir — isim etiketli listede gelmeye devam eder; istemci 1.1.21'e
geçtiğinde yeniden görünür.

## Değişen / yeni dosyalar

- files/functions/anime_helpers.php (tercih ailesi dil koduna geçti; display_title etiket okuyor; adult_mask_related etiketli listeyi temizliyor)
- files/functions/title_lang_helpers.php (alt_titles_for_form kaldırıldı — işi migration'a geçti)
- files/functions/series_helpers.php (üç sorgu + display_related_title köprüsü)
- files/functions/synopsis_helpers.php (konu içi link sorgusu)
- files/set_title_pref.php (boolean yerine dil kodu, beyaz listeli)
- files/list_settings.php (onay kutusu → dil seçici; içe/dışa aktarmadan kolon çıktı)
- files/statistics.php (eksik title_pref_init eklendi + sorgu)
- files/anime_details.php, files/chronology.php, files/series_timeline.php, files/pending.php (sorgular)
- files/add_anime.php, files/edit_anime.php (kolon artık yazılmıyor)
- files/catalog_import.php, files/admin/catalog_push.php, files/admin/admin_catalog_requests.php, files/admin/admin_sync_example.php (katalog telinden çıkarıldı)
- catalog_server/catalog.php, catalog_server/admin_push.php (sunucu tarafı tel)
- files/css/components.css (artık yanıltıcı olan sınıf yorumu)
- files/lang/tr.php, files/lang/en.php (dil seçici metinleri + yardım sayfası)
- files/schema.sql (kolon tanımları ve açıklamalar kaldırıldı)
- files/migration/1.1.21/upgrade.sql (yeni)
- files/version.txt
#16
Announcements & Releases / v1.1.21
Last post by hitsumo - Jul 25, 2026, 02:20 PM
# Anime Tracker 1.1.21

**Release date:** 2026-07-24

## New: Title Language — no longer English-only

- **The "show English titles" checkbox in List Settings is now a real language
  picker.** Choose which language you want anime titles shown in: **Romaji**
  (default), English, Japanese, Turkish, Chinese, Korean, French.
- **Anime with no title in the language you picked stay in Romaji.** Choosing a
  language the catalog does not carry yet is harmless — nothing ever renders
  blank.
- **Where do those languages come from?** From what 1.1.20 added: the language
  box next to each alternative title on the add/edit form. Give an anime a
  Japanese name and mark it "Japanese", and everyone with Title Language set to
  Japanese sees it.
- The preference is yours alone and **independent of the interface language** —
  you can use a Turkish interface while reading Japanese titles.

## Fixed: the statistics page ignored your title language

- **The "Recently Watched" table always showed Romaji, whatever your preference
  was.** 1.1.18 reworked that cell to support the title-language preference but
  never added the call that loads it, so the table silently fell back to the
  default. It now honours the setting.

## Removed: the title_english column

- **The column we said was "staying for now" in 1.1.20 has been retired.** A
  title's language now lives in exactly one place: the tagged
  alternative-titles list.
- **Why did it stay, and why is it gone now?** In 1.1.20 the column was a
  display shortcut derived from the `[en]` tag on save, which is what let that
  release ship without touching a single title-rendering page. But it could
  only ever describe English, so other languages could be **stored** and not
  **shown**. Now that display reads the tags directly, the column had no job
  left.
- **No data is lost.** English names already live in the alternative-titles
  list as `[en]` entries, and the migration tags any row that slipped through
  before dropping the column.

## How it works (technical)

- The preference now holds a language code (`display_title_lang`); it used to
  be a boolean (`display_title_english`). The migration moves everyone who had
  it switched on to English.
- `display_title()` looks for the chosen language's tag and falls back to the
  Romaji title when there isn't one. With the default Romaji preference the
  tagged list is **never parsed at all**, so the most common case costs nothing.
- Every query that renders a title now selects `alternative_titles` instead of
  `title_english`: anime details, statistics, series timeline, chronology,
  pending approvals, and in-synopsis anime links.
- Adult masking got stronger: it used to clear only the English title, and now
  clears the whole tagged list — otherwise an anime whose name is hidden could
  leak it to a viewer reading titles in another language.

## Schema / migration

- `migration/1.1.21/upgrade.sql` **drops the column** (from `animes` and
  `catalog_requests`). Two rescue steps run first: if the English name sits in
  the list untagged it is tagged in place, and if it is missing entirely it is
  appended as `[en]`. This is necessary because a catalog sync performed after
  1.1.20 may have overwritten the tags with the server's older copy.
- The preference migration is in the same file: users with
  `display_title_english = '1'` become `display_title_lang = 'en'`, and the old
  rows are deleted.
- **The migration is restartable.** Its first act is to re-add the column (a
  "duplicate column" error is ignored when it is still there). So a half-finished
  upgrade, or a column dropped by hand, cannot lock the upgrade out — the rescue
  steps find a column to read, it is dropped again at the end, and the outcome is
  identical either way.

## Manual work IS required on the central catalog server

1.1.20 was the exception that needed none; this release needs it again.
**The order is critical:**

1. Deploy the new `catalog_server/` (`catalog.php` + `admin_push.php`).
2. On the server database: `ALTER TABLE animes DROP COLUMN title_english;`
3. Deploy the app (the migration runs by itself).
4. Full catalog push.

In the reverse order (ALTER first) the old `catalog.php` would try to read a
dropped column and **sync would break for every client**. The server has no
`catalog_requests` table, so only that single `ALTER` runs there.

**Installs left behind:** a client still on 1.1.20 or earlier no longer
receives an English title from the catalog, so titles fall back to Romaji
there. Nothing is lost — the name still arrives in the tagged list, and it
reappears once that client updates.

## Changed / new files

- files/functions/anime_helpers.php (preference family now holds a language code; display_title reads tags; adult_mask_related clears the tagged list)
- files/functions/title_lang_helpers.php (alt_titles_for_form removed — its job moved into the migration)
- files/functions/series_helpers.php (three queries + the display_related_title bridge)
- files/functions/synopsis_helpers.php (in-synopsis link query)
- files/set_title_pref.php (a whitelisted language code instead of a boolean)
- files/list_settings.php (checkbox → language picker; column dropped from import/export)
- files/statistics.php (missing title_pref_init added + query)
- files/anime_details.php, files/chronology.php, files/series_timeline.php, files/pending.php (queries)
- files/add_anime.php, files/edit_anime.php (the column is no longer written)
- files/catalog_import.php, files/admin/catalog_push.php, files/admin/admin_catalog_requests.php, files/admin/admin_sync_example.php (removed from the catalog wire)
- catalog_server/catalog.php, catalog_server/admin_push.php (server-side wire)
- files/css/components.css (a class comment that had become misleading)
- files/lang/tr.php, files/lang/en.php (picker strings + help page)
- files/schema.sql (column definitions and notes removed)
- files/migration/1.1.21/upgrade.sql (new)
- files/version.txt
#17
Announcements & Releases / v1.1.20
Last post by hitsumo - Jul 25, 2026, 02:20 PM
# Anime Tracker 1.1.20

**Release date:** 2026-07-24

## Changed: the "English Title" field is gone; language moved onto alternative titles

- **The separate "English Title" field has been removed from the add/edit
  form.** All of an anime's names are now entered in one place: the
  alternative-titles list, using the language box next to each row.
 
- **Every alternative title can be marked with a language:**
 
  ```
  Anime Title       : Tonari no Totoro
  Alternative Titles: My Neighbor Totoro    [English]
                      となりのトトロ          [Japanese]
                      Totoro                [No language set]
  ```
 
- **The name you mark "English" does exactly what that field used to do:** it
  is shown instead of the Romaji title while "show English titles" is enabled
  in List Settings. Same behaviour, one place to type it.
 
- **Picking a language is optional.** Leave the box on "— No language set" and
  the alternative title behaves as it always did.
 
- **Languages available:** English, Japanese, Turkish, Chinese, Korean, French.
 
- **Bonus - search got wider.** The list's search box looks inside the anime
  title and the alternative titles, but never inside the English Title field.
  Now that the English name is part of the list, searching for
  "My Neighbor Totoro" finds the anime.
 

## Why not just another field per language

- **Wanting a Turkish title is where the old design dead-ended.** The "English
  Title" field only ever described one language; Turkish would need a second
  field, Japanese a third. Each one meant a new database column, a new branch
  in the title-display code, a new field on the catalog wire and a manual
  change on the central catalog host - once per language.
- **So the language moved into the list.** Adding a language is now a one-line
  change: one entry in the map in `title_lang_helpers.php` and one line in the
  language files. The database is not touched.

## How it is stored (technical)

- The language rides in the existing `animes.alternative_titles` text as an
  optional `[xx]` tag in front of each name:
 
  ```
  [en]My Neighbor Totoro|[ja]となりのトトロ|Totoro
  ```
 
- **Why `[en]` and not `en:`?** A bare "two letters and a colon" prefix would
  misread real titles - `Re:Zero kara Hajimeru Isekai Seikatsu` starts with
  exactly that pattern. The bracket form plus a whitelist check (only a known
  language code counts as a tag) guarantees an untagged title can never be
  mistaken for a tagged one. Names like `[TV] Bleach` also survive intact.
 
- New helper file: `files/functions/title_lang_helpers.php`.
  `build_alt_titles()` produces the column value, `parse_alt_titles()` turns it
  back into form rows, and `alt_title_for_lang()` reads one language's name.
 
- A hand-typed `[en]` prefix is stripped on save (the dropdown is the single
  authority), and a `|` typed into a name becomes a space, since it is the
  separator and would otherwise split the name in two.
 
- The `animes.title_english` column **stays**, but the user no longer fills it:
  it is derived on save from the `[en]`-tagged entry in the list. That is why
  this release touches no title-rendering page at all. Moving display onto the
  tags and retiring the column is left to 1.1.21.
 
- Rows that arrive from the catalog without tags but with a populated
  `title_english` are shown pre-marked as English in the edit form. Without
  that, opening the form and saving would silently wipe the English name.
 

## Schema / migration

- `migration/1.1.20/upgrade.sql` makes **no schema change** - no new table,
  column or settings key. Its two `UPDATE` statements add the English name,
  which until now lived only in `title_english`, to the list as an `[en]`-tagged
  entry so search picks it up immediately. Both statements clear their own
  condition, so re-running the migration cannot add a second copy.
- **No manual work is needed on the central catalog server.**
  `alternative_titles` was already a text column and its type did not change,
  so tagged text rides the existing sync chain untouched. (Unlike 1.1.3, 1.1.10
  and 1.1.17, which each required a manual ALTER.)
- One note: after this install pushes to the catalog, a client still on 1.1.19
  sees the `[en]` prefix as raw text in its own edit form. Search and display
  are unaffected and no data is lost; the prefix becomes a tag once that client
  updates.

## Changed / new files

- files/functions/title_lang_helpers.php (new; tag parsing and building)
- files/functions.php (title_lang_helpers added to the loader)
- files/add_anime.php ("English Title" field removed; per-row language box)
- files/edit_anime.php (same, plus restoring existing rows' tags)
- files/js/anime_form.js (a new row is now built with its language box)
- files/css/list.css (language box width + row wrapping on narrow screens)
- files/lang/tr.php, files/lang/en.php (language names; title_english keys removed)
- files/schema.sql (alternative_titles and title_english notes updated)
- files/migration/1.1.20/upgrade.sql (new)
- files/version.txt
#18
Duyurular ve Sürümler / v1.1.20
Last post by hitsumo - Jul 25, 2026, 02:19 PM
# Anime Tracker 1.1.20

**Yayın tarihi:** 2026-07-24

## Değişti: "İngilizce Başlık" kutusu kaldırıldı, dil alternatif isimlere taşındı

- **Anime ekleme/düzenleme formundaki ayrı "İngilizce Başlık" alanı kaldırıldı.**
  Bir animenin bütün isimleri artık tek bir yerde girilir: alternatif isim
  listesinde, her satırın yanındaki dil kutusuyla.
 
- **Her alternatif ismin dili işaretlenebilir:**
 
  ```
  Anime İsmi        : Tonari no Totoro
  Alternatif İsimler: My Neighbor Totoro    [İngilizce]
                      となりのトトロ          [Japonca]
                      Totoro                [Dil belirtilmedi]
  ```
 
- **"İngilizce" işaretlediğiniz isim, eskiden o kutunun yaptığı işi yapar:**
  Liste Ayarları'nda "İngilizce başlıkları göster" açıkken listede Romaji
  başlık yerine o gösterilir. Yani davranış aynı, giriş yeri tek.
 
- **Dil seçmek zorunlu değil.** Bir isme dil atamak istemiyorsanız kutu
  "— Dil belirtilmedi"de kalır; alternatif isim eskisi gibi çalışır.
 
- **Seçilebilen diller:** İngilizce, Japonca, Türkçe, Çince, Korece, Fransızca.
 
- **Bonus — arama genişledi.** Ana listedeki arama kutusu anime ismi ve
  alternatif isimler içinde arıyor, İngilizce başlık kutusuna yazdıklarınızda
  ise aramıyordu. İngilizce isim artık listenin bir parçası olduğu için
  "My Neighbor Totoro" araması sonuç veriyor.
 

## Neden dil başına yeni bir kutu değil

- **Türkçe başlık istendiğinde eski yol tıkanıyordu.** "İngilizce Başlık"
  kutusu tek bir dili anlatan çıkmaz bir sokaktı; Türkçe için ikinci bir kutu,
  Japonca için üçüncüsü gerekirdi. Her biri veritabanında yeni bir kolon,
  başlık gösteren koda yeni bir dal, katalog senkronuna yeni bir alan ve
  merkez katalog sunucusunda elle bir müdahale demekti — dil başına bir kez.
- **Dil, listenin içine taşındı.** Yeni bir dil eklemek artık tek satırlık bir
  iş: `title_lang_helpers.php` içindeki haritaya bir satır, dil dosyalarına bir
  satır. Veritabanına dokunulmaz.

## Nasıl saklanıyor (teknik)

- Dil bilgisi mevcut `animes.alternative_titles` metnine, her ismin önüne
  konan isteğe bağlı bir `[xx]` etiketiyle yazılır:
 
  ```
  [en]My Neighbor Totoro|[ja]となりのトトロ|Totoro
  ```
 
- **Neden `en:` değil de `[en]`?** Yalın "iki harf + iki nokta" öneki gerçek
  başlıkları yanlış okurdu — `Re:Zero kara Hajimeru Isekai Seikatsu` tam olarak
  bu kalıpla başlıyor. Köşeli parantez artı beyaz liste kontrolü (yalnızca
  tanınan bir dil kodu etiket sayılır) etiketsiz bir başlığın asla etiketli
  sanılmamasını garanti eder. `[TV] Bleach` gibi isimler de olduğu gibi kalır.
 
- Yeni yardımcı dosya: `files/functions/title_lang_helpers.php`. Kolon değerini
  `build_alt_titles()` üretir, `parse_alt_titles()` forma geri çözer,
  `alt_title_for_lang()` bir dilin ismini okur.
 
- Elle yazılan `[en]` öneki kaydederken temizlenir (dil kutusu tek yetkilidir),
  isme yazılan `|` işareti boşluğa çevrilir (ayraç olduğu için ismi ikiye
  bölerdi).
 
- `animes.title_english` kolonu **duruyor** ama artık kullanıcı doldurmuyor:
  kaydetme anında listedeki `[en]` etiketli isimden türetiliyor. Böylece bu
  sürüm başlık gösteren tek bir sayfaya bile dokunmadı. Gösterimin doğrudan
  etiketlere bağlanması ve kolonun emekli edilmesi 1.1.21'e bırakıldı.
 
- Katalogdan gelip henüz etiket taşımayan (ama `title_english` dolu) kayıtlar
  düzenleme formunda otomatik olarak İngilizce işaretli görünür. Bu olmasaydı
  kullanıcı formu açıp kaydettiğinde İngilizce isim sessizce silinirdi.
 

## Şema / migration

- `migration/1.1.20/upgrade.sql` **şema değiştirmez** — yeni tablo, kolon veya
  ayar anahtarı yok. İçindeki iki `UPDATE`, mevcut kayıtlarda yalnızca
  `title_english`'te duran İngilizce ismi listeye `[en]` etiketli olarak ekler
  (aramanın hemen çalışması için). İkisi de kendi koşulunu sıfırladığından
  migration tekrar çalışsa bile ikinci bir kopya oluşmaz.
- **Merkez katalog sunucusunda elle işlem GEREKMEZ.** `alternative_titles`
  zaten bir metin kolonu ve tipi değişmedi; etiketli metin mevcut senkron
  zincirinden olduğu gibi geçer. (1.1.3, 1.1.10 ve 1.1.17'nin aksine.)
- Tek gözlem: bu kurulum katalogu push ettikten sonra henüz 1.1.20'ye
  geçmemiş bir istemci `[en]` önekini kendi düzenleme formunda ham metin
  olarak görür. Arama ve gösterim etkilenmez, veri kaybı yoktur; istemci
  güncellenince önek etikete döner.

## Değişen / yeni dosyalar

- files/functions/title_lang_helpers.php (yeni; etiket ayrıştırma/kurma)
- files/functions.php (loader'a title_lang_helpers eklendi)
- files/add_anime.php ("İngilizce Başlık" alanı kaldırıldı; satır başına dil kutusu)
- files/edit_anime.php (aynısı + mevcut kayıtların etiketlerinin geri yüklenmesi)
- files/js/anime_form.js (yeni satır artık dil kutusuyla üretiliyor)
- files/css/list.css (dil kutusu genişliği + dar ekranda satır sarması)
- files/lang/tr.php, files/lang/en.php (dil adları; title_english anahtarları kaldırıldı)
- files/schema.sql (alternative_titles ve title_english açıklamaları güncellendi)
- files/migration/1.1.20/upgrade.sql (yeni)
- files/version.txt
#19
Duyurular ve Sürümler / v1.1.19
Last post by hitsumo - Jul 25, 2026, 02:18 PM
# Anime Tracker 1.1.19

**Yayın tarihi:** 2026-07-22

## Yeni: Konu içinde başka bir animeye tıklanabilir link

- **Bir animenin konusundan (synopsis) başka bir animeye tıklanabilir link
  verebilirsiniz.** Örneğin bir filmin konusuna "X animesinin film olarak
  derlenmiş hâli" yazıp "X animesi" kısmını, tıklandığında o animenin detay
  sayfasını açan bir bağlantı yapabilirsiniz.
 
- **Yazım biçimi bir kısa koddur:**
 
  ```
  [[anime:52991|Frieren]] animesinin film olarak derlenmiş hâli.
  ```
 
  Buradaki `52991` hedef animenin **MyAnimeList (MAL) numarasıdır**, `Frieren`
  ise ekranda görünecek bağlantı metnidir.
 
- **Metni yazmazsanız hedef animenin kendi başlığı otomatik kullanılır:**
  `[[anime:52991]]` yazmak, bağlantıyı animenin adıyla etiketler.
 
- **Hem katalog konusunda hem de kişisel notunuzda çalışır.**
 
- Bağlantı metni sitenin normal link rengiyle, alttan çizili gösterilir; yazının
  akışını bozmayacak kadar sade tutuldu.
 

## Neden ham HTML değil, kısa kod?

- **Konuya doğrudan `<a href="...">` yazmak mümkün değildir; bilerek böyle.**
  Konu metni katalog üzerinden bütün üyelere gider; ham HTML'e izin vermek
  depolanmış XSS ve rastgele dış bağlantı riski açardı. Kısa kod yalnızca bir
  MAL numarası taşır, geri kalan her şey güvenle kaçırılmaya devam eder ve
  bağlantının gittiği yer **her zaman sitenin kendi anime detay sayfasıdır**.
- **Numara olarak neden yerel id değil, MAL id?** Katalog metni her kuruluma
  aynı gider, ama animelerin yerel id'si her kurulumda ayrı atanır. Yerel id
  yazsaydınız, senkron sonrası başka kurulumda yanlış animeye giderdi. MAL
  numarası evrensel olduğu için bağlantı her kurulumda doğru animeyi bulur.
- **Aradığı anime o kurulumda yoksa link kırılmaz:** yazdığınız metin düz yazı
  olarak kalır, cümle yine okunur.

## Nasıl çalışır (teknik)

- Yeni bir yardımcı dosya eklendi: `files/functions/synopsis_helpers.php`.
  `render_synopsis()` konuyu güvenli HTML olarak basar ve `[[anime:..]]`
  kısa kodlarını yerel `anime_details.php` bağlantısına çevirir; `synopsis_plain()`
  ise önizleme/kısaltma yerleri için kısa kodu düz etikete indirger.
- Metnin tamamı önce `htmlspecialchars` ile kaçırılır, kısa kod ondan sonra
  bağlantıya dönüştürülür; yani önceki `nl2br(htmlspecialchars(...))` davranışı
  düz konularda birebir korunur.
- Kısa kod içindeki MAL numaraları tek bir toplu sorguyla (`WHERE mal_id IN (...)`)
  yerel satırlara çözülür; her bağlantı için ayrı sorgu açılmaz.
- Sürpriz öneri kartındaki 200 karakterlik konu özeti artık `synopsis_plain()`
  ile üretilir; böylece özet ham `[[...]]` göstermez ve kısaltma bir kısa kodu
  ortadan bölmez.

## Şema / migration

- `migration/1.1.19/upgrade.sql` yalnızca sürümü 1.1.19'a taşır; **şema
  değişikliği yoktur** (çalıştırılacak SQL ifadesi yok). Özellik tamamen render
  katmanındadır. Merkez katalog etkilenmez, sunucuda elle bir işlem **gerekmez**.

## Değişen / yeni dosyalar

- files/functions/synopsis_helpers.php (yeni; `render_synopsis` + `synopsis_plain`)
- files/functions.php (loader'a synopsis_helpers eklendi)
- files/anime_details.php (katalog ve kişisel konu artık `render_synopsis()` ile basılıyor)
- files/recommendations.php (sürpriz özeti kısaltmadan önce `synopsis_plain()`)
- files/css/base.css (`.synopsis-link` stili)
- files/migration/1.1.19/upgrade.sql (yeni)
- files/version.txt
#20
Announcements & Releases / v1.1.19
Last post by hitsumo - Jul 25, 2026, 02:17 PM
# Anime Tracker 1.1.19

**Release date:** 2026-07-22

## New: clickable anime links inside a synopsis

- **A synopsis can now link to another anime as clickable text.** For example,
  a film's synopsis can read "the movie recap of X" and turn the "X" into a link
  that opens that anime's detail page.
 
- **The syntax is a shortcode:**
 
  ```
  The movie recap of [[anime:52991|Frieren]].
  ```
 
  Here `52991` is the target anime's **MyAnimeList (MAL) id**, and `Frieren` is
  the link text shown on screen.
 
- **Leave the text out and the target's own title is used automatically:**
  `[[anime:52991]]` labels the link with the anime's name.
 
- **Works in both the catalog synopsis and your personal note.**
 
- The link is shown in the site's normal link colour with an underline, kept
  subtle so it reads as part of the prose rather than a button.
 

## Why a shortcode and not raw HTML

- **Typing `<a href="...">` directly into a synopsis is not possible, by
  design.** Synopsis text travels over the catalog to every member; allowing raw
  HTML would open stored XSS and let arbitrary external URLs ride along. The
  shortcode carries only a MAL id, everything else stays escaped, and the link
  target is **always the site's own anime detail page**.
- **Why the MAL id and not the local id?** Catalog text is served identically to
  every instance, but each instance assigns its own local anime ids. A raw local
  id would point at the wrong anime elsewhere after a sync. The MAL id is global,
  so the link resolves to the right anime on every instance.
- **If that anime isn't in this catalog the link doesn't break:** your text is
  kept as plain prose and the sentence still reads.

## How it works (technical)

- A new helper file was added: `files/functions/synopsis_helpers.php`.
  `render_synopsis()` renders the synopsis as safe HTML and turns `[[anime:..]]`
  shortcodes into local `anime_details.php` links; `synopsis_plain()` reduces the
  shortcode to its plain label for preview/truncation surfaces.
- The whole string is `htmlspecialchars`-escaped first, and the shortcode is
  turned into a link afterwards, so the previous `nl2br(htmlspecialchars(...))`
  behaviour is preserved exactly for plain synopses.
- MAL ids in the text are resolved to local rows in a single batched query
  (`WHERE mal_id IN (...)`), not one query per link.
- The 200-character synopsis teaser on the surprise recommendation card now goes
  through `synopsis_plain()`, so the teaser never shows raw `[[...]]` and
  truncation never slices a shortcode in half.

## Schema / migration

- `migration/1.1.19/upgrade.sql` only moves the version to 1.1.19; there is **no
  schema change** (no SQL statements to run). The feature lives entirely in the
  render layer. The central catalog is unaffected and no manual step is needed on
  the server.

## Changed / new files

- files/functions/synopsis_helpers.php (new; `render_synopsis` + `synopsis_plain`)
- files/functions.php (synopsis_helpers added to the loader)
- files/anime_details.php (catalog and personal synopsis now rendered via `render_synopsis()`)
- files/recommendations.php (surprise teaser runs through `synopsis_plain()` before truncation)
- files/css/base.css (`.synopsis-link` style)
- files/migration/1.1.19/upgrade.sql (new)
- files/version.txt