diff --git a/en/blog/index.html b/en/blog/index.html index 61defe41..e037a18e 100644 --- a/en/blog/index.html +++ b/en/blog/index.html @@ -237,6 +237,15 @@
+ +
+
+ Student life +

Where to search for student accommodation in Slovenia

+

University housing, trusted rental sites, community groups, and what to check before you pay or sign — so you avoid scams and settle in safely.

+ 7 min read +
+
diff --git a/en/blog/where-to-search-for-accommodation-in-slovenia/index.html b/en/blog/where-to-search-for-accommodation-in-slovenia/index.html new file mode 100644 index 00000000..8d1479f4 --- /dev/null +++ b/en/blog/where-to-search-for-accommodation-in-slovenia/index.html @@ -0,0 +1,432 @@ + + + + + + Where to search for student accommodation in Slovenia - MSOS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

Select Language

+ +
+ +
+
+ + +
+ +
+ Where to search for student accommodation in Slovenia + +
+
+
+ +
+ +
+
+ +
+ + + +
+
+

Student life · Last checked 3 August 2026

+

Where to search for student accommodation in Slovenia

+

Finding accommodation is often one of the most stressful parts of moving to Slovenia, especially in Ljubljana and on the coast. The best approach is to begin early, check several sources at the same time, and never send money before confirming who is renting the property and whether you will receive a proper contract.

+
+ +
+ Macedonian students settling into accommodation in Slovenia +
Illustrative image · Placeholder image — to be replaced with a photo of student housing.
+
+ +
+ +

Finding accommodation is often one of the most stressful parts of moving to Slovenia, especially in Ljubljana and on the coast. The best approach is to begin early, check several sources at the same time, and never send money before confirming who is renting the property and whether you will receive a proper contract.

+

1. Start with your university

+

Before searching private listings, check the accommodation information published by your university. University pages explain whether international students can apply for a dormitory, which student residences are available, and who to contact for help.

+

The University of Ljubljana generally directs international degree students toward private accommodation and recommends the free room agency m2, as well as its HousingAnywhere rental platform. The University of Maribor offers limited dormitory places and also publishes private and student-residence options. The University of Primorska has specific calls for some international degree students, while exchange students are usually directed toward private housing. The University of Nova Gorica lists housing options across Nova Gorica, Vipava, Ajdovščina, Postojna, and nearby Gorizia, and its student office also helps connect students with accommodation providers.

+

Official university pages:

+ +

Do not assume that a dormitory place is guaranteed. Eligibility and availability vary, and places can fill quickly.

+

2. Use established rental and student-housing websites

+

Several websites are useful for finding rooms, roommates, studios, and apartments across Slovenia.

+

Nepremicnine.net is one of the largest Slovenian property portals. It is useful for full apartments and rooms, and it allows you to filter by city, size, price, and property type.

+

Nepremicnine.net

+

Bolha has a large property section with private listings from across Slovenia. Read each advertisement carefully because both agencies and private landlords publish there.

+

Bolha real estate

+

Mojcimer focuses on student rooms, shared apartments, and finding roommates. It is especially useful when you are open to sharing accommodation.

+

Mojcimer

+

m2 is a free room-rental service recommended by the University of Ljubljana. It can help students search for private accommodation and understand common rental questions.

+

m2 room agency

+

Kamrica is particularly useful for accommodation in Maribor and includes rooms, beds, and student apartments.

+

Kamrica

+

HousingAnywhere is an international rental platform often used by incoming and exchange students. It can be useful for arranging accommodation before arrival, but always check platform fees, cancellation rules, and what is included in the price.

+

HousingAnywhere

+

3. Facebook groups and community channels

+

Facebook groups and Marketplace can help you find new listings quickly, especially from students looking for roommates or leaving their room before the end of a contract.

+

Useful search terms include:

+
    +
  • Stanovanja Ljubljana
  • +
  • Sobe Ljubljana
  • +
  • Stanovanja Maribor
  • +
  • Študentske sobe Maribor
  • +
  • Oddaja in najem stanovanj Koper
  • +
  • Stanovanja Obala
  • +
  • Oddaja in najem stanovanj Nova Gorica
  • +
  • Apartments and rooms for rent in Ljubljana
  • +
  • Erasmus accommodation Ljubljana, Maribor, or Koper
  • +
+

You can also check Facebook Marketplace and university or Erasmus student groups.

+

Treat social-media listings with extra caution. The University of Primorska specifically warns that Facebook accommodation groups may not always be reliable. A profile, photograph, or friendly conversation does not prove that the person owns the property or has the right to rent it.

+

Ask local student organisations, ESN, classmates, or MSOS members whether they recognise the landlord, address, or listing before paying anything.

+

4. What to check before paying or signing

+

A legitimate offer should clearly explain the monthly rent, deposit, utilities, duration of the rental, notice period, and what furniture or equipment is included. Ask whether electricity, heating, water, internet, building costs, and cleaning are included or paid separately.

+

Before transferring money, verify the exact address and request a live viewing, video call, or visit by someone you trust. Confirm the landlord’s identity and ask for a written rental contract. The contract should identify the landlord and tenant, describe the room or apartment, state the rent and deposit, list additional costs, define the rental period, and explain the termination conditions.

+

Be careful when:

+
    +
  • the price is much lower than similar listings
  • +
  • the landlord refuses a viewing or video call
  • +
  • you are pressured to pay immediately
  • +
  • payment is requested through an unusual transfer method
  • +
  • the landlord refuses to provide a contract
  • +
  • the person says they are abroad and cannot prove ownership
  • +
  • the photographs or description appear copied from another listing
  • +
  • the landlord refuses temporary-address registration
  • +
+

Do not send a passport scan to an unknown person before verifying the offer. When a copy is genuinely needed, ask why and how it will be stored.

+

5. Why the contract and address matter for residence procedures

+

Accommodation is not only about having a place to sleep. Your address may also be needed for immigration and administrative procedures.

+

Depending on your entry and application route, an embassy or authority may request proof of accommodation. After receiving a temporary residence permit, foreigners must register their temporary address in Slovenia within the applicable deadline. Official guidance currently states that the temporary residence should generally be registered within eight days after the residence permit is delivered.

+

Before signing, ask the landlord directly:

+ +

Avoid accommodation where the landlord refuses prijava začasnega prebivališča. Even if the room looks good, this can create serious problems with residence registration, official mail, banking, tax procedures, and other student administration.

+

Keep copies of the signed rental contract, proof of deposit and rent payments, any landlord statement, the temporary-address registration confirmation, photographs of the room at move-in, and an inventory of furniture and existing damage.

+

6. Need more help?

+

For detailed information about dormitory eligibility, private rental contracts, temporary-address registration, city differences, and accommodation scams, read the MSOS Student Welcome Guide.

+

MSOS can share student experience and help you understand where to look, but the university, landlord, upravna enota, or other responsible institution must confirm your individual administrative requirements.

+

# Sources

+ +

Last checked: 3 August 2026

+
+ +
+
+
+
+ MSOS emblem +
+

MSOS Team

+

Macedonian Student Organisation in Slovenia

+
+
+ +
+
+
+
+ + +
+ + + + + + + + + + + diff --git a/tools/build_blog.py b/tools/build_blog.py new file mode 100644 index 00000000..cbd1f066 --- /dev/null +++ b/tools/build_blog.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +""" +build_blog.py -- turn a Markdown blog source into a site blog article page. + +It clones an existing blog page of the SAME language (so the header, footer, +SEO markers, scripts and layout are already correct) and swaps in: + - and the mobile article title + - the article header (category + date, H1, subtitle) + - the hero <figure> (image + illustrative caption) + - the scrollspy sidebar nav (built from the ## headings) + - the <div class="article-body"> content (converted from Markdown) + +Run seo_inject.py afterwards to refresh canonical/hreflang/meta for new slugs. + +Usage (see tools/blog_manifest.py for the per-post config the runner passes in): + from build_blog import build_page + build_page(cfg) +""" +import io, os, re, html + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# ---- tiny Markdown -> article-body HTML converter -------------------------- + +_INLINE = [ + (re.compile(r'\[([^\]]+)\]\((https?://[^)\s]+)\)'), + r'<a href="\2" target="_blank" rel="noopener">\1</a>'), + (re.compile(r'\[([^\]]+)\]\((?!https?://)([^)\s]+)\)'), r'<a href="\2">\1</a>'), + (re.compile(r'\*\*([^*]+)\*\*'), r'<strong>\1</strong>'), + (re.compile(r'(?<!\*)\*(?!\*)([^*]+)\*(?!\*)'), r'<em>\1</em>'), +] + +def _inline(text): + text = html.escape(text, quote=False) + # unescape the markdown link/format markers we still need to match + text = text.replace("&", "&") + for rx, rep in _INLINE: + text = rx.sub(rep, text) + return text + +def _slug(text): + s = re.sub(r'[^a-z0-9]+', '-', text.lower()).strip('-') + return s or "section" + +def preprocess(md): + """Strip metadata lines, the 'Hero image placeholder' section and rule lines + so only real article content remains.""" + out, skip_hero = [], False + for ln in md.replace("\r\n", "\n").split("\n"): + s = ln.strip() + if s.startswith("## ") and re.search(r'hero image', s, re.I): + skip_hero = True + continue + if skip_hero: + if s.startswith("## ") or s == "---": + skip_hero = False + if s.startswith("## "): + out.append(ln) + continue + if re.match(r'^\*\*[^*]+:\*\*\s', s) and len(s) < 90: # metadata like "**Category:** X" + continue + if s == "---": + continue + out.append(ln) + return "\n".join(out) + + +def parse_markdown(md): + """Return (title, subtitle, sections) where sections=[(id,heading,html)] and + the first section id may be '' for intro content before the first ##.""" + md = preprocess(md) + lines = md.split("\n") + title = "" + subtitle = "" + # strip a leading metadata block ("**Category:** ..." lines) if present + body_lines, i = [], 0 + # title = first # heading + for i, ln in enumerate(lines): + if ln.startswith("# "): + title = ln[2:].strip() + body_lines = lines[i+1:] + break + else: + body_lines = lines + + sections = [] # list of dict(id, heading, blocks[]) + cur = {"id": "", "heading": None, "blocks": []} + def flush(): + if cur["heading"] is not None or cur["blocks"]: + sections.append(dict(cur)) + + para, list_items, list_type, quote = [], [], None, [] + def flush_blocks(): + nonlocal para, list_items, list_type, quote + if para: + txt = " ".join(para).strip() + if txt: + cur["blocks"].append(("p", txt)) + para = [] + if list_items: + cur["blocks"].append((list_type, list_items)) + list_items, list_type = [], None + if quote: + cur["blocks"].append(("quote", " ".join(quote).strip())) + quote = [] + + for ln in body_lines: + s = ln.rstrip().strip() + if s.startswith("## "): + flush_blocks(); flush() + h = s[3:].strip() + cur = {"id": _slug(h), "heading": h, "blocks": []} + continue + if s.startswith("### "): + flush_blocks() + cur["blocks"].append(("h3", s[4:].strip())) + continue + if s.startswith(">"): + content = s[1:].strip() + if content: + quote.append(content) + continue + m = re.match(r'^(\d+)\.\s+(.*)', s) + if m: + if list_type not in (None, "ol"): flush_blocks() + list_type = "ol"; list_items.append(m.group(2)); continue + m = re.match(r'^[-*]\s+(.*)', s) + if m: + if list_type not in (None, "ul"): flush_blocks() + list_type = "ul"; list_items.append(m.group(1)); continue + if s == "": + flush_blocks(); continue + # normal paragraph line + if list_items or quote: + flush_blocks() + para.append(s) + flush_blocks(); flush() + + # subtitle = first paragraph of the intro section (often bold lede) + for sec in sections: + for kind, val in sec["blocks"]: + if kind == "p": + subtitle = re.sub(r'\*\*|\*', '', val).strip() + break + if subtitle: + break + return title, subtitle, sections + + +def render_body(sections, category, date_label, last_checked_label): + out = [] + for idx, sec in enumerate(sections): + if sec["heading"]: + out.append(f' <h2 id="{sec["id"]}">{_inline(sec["heading"])}</h2>') + for kind, val in sec["blocks"]: + if kind == "p": + out.append(f' <p>{_inline(val)}</p>') + elif kind == "h3": + out.append(f' <h3>{_inline(val)}</h3>') + elif kind == "quote": + out.append(' <aside class="guide-box guide-box--warning">') + out.append(f' <p>{_inline(val)}</p>') + out.append(' </aside>') + elif kind in ("ul", "ol"): + out.append(f' <{kind}>') + for it in val: + out.append(f' <li>{_inline(it)}</li>') + out.append(f' </{kind}>') + out.append(f' <p class="guide-last-checked">{last_checked_label}</p>') + return "\n".join(out) + + +def build_scrollspy(sections): + links = [] + first = True + for sec in sections: + if not sec["heading"]: + continue + cls = ' class="active"' if first else "" + links.append(f' <li><a href="#{sec["id"]}"{cls}>{_inline(sec["heading"])}</a></li>') + first = False + return "\n".join(links) + + +def build_page(cfg): + """cfg keys: base (path), out (path), title, category, date_label, + last_checked, hero_img, hero_alt, hero_caption, md (markdown text).""" + title, subtitle, sections = parse_markdown(cfg["md"]) + title = cfg.get("title") or title + subtitle = cfg.get("subtitle") or subtitle + + base = io.open(cfg["base"], encoding="utf-8").read() + + # <title> + base = re.sub(r'<title>.*?', + f'{html.escape(title)} - MSOS', base, count=1, flags=re.DOTALL) + # mobile article title + base = re.sub(r'().*?()', + lambda m: m.group(1) + html.escape(title) + m.group(2), base, count=1, flags=re.DOTALL) + # article header block + header = ( + f'

{_inline(cfg["date_label"])}

\n' + f'

{_inline(title)}

\n' + f'

{_inline(subtitle)}

' + ) + base = re.sub(r'

.*?

\s*

.*?

\s*

.*?

', + header, base, count=1, flags=re.DOTALL) + # hero figure + fig = ( + f'
\n' + f' {html.escape(cfg[\n' + f'
{cfg["illus_label"]} · {cfg["hero_caption"]}
\n' + f'
' + ) + base = re.sub(r'
.*?
', fig, base, count=1, flags=re.DOTALL) + # scrollspy nav + nav = ' ' + base = re.sub(r'(