Compare commits
29 Commits
bea593732d
...
b4359c0458
| Author | SHA1 | Date | |
|---|---|---|---|
|
b4359c0458
|
|||
| 4dabcaa13c | |||
| 2268334c30 | |||
| 645b8b070b | |||
| adb306604c | |||
| 2e4d074bfc | |||
| 025db00c6a | |||
| 92f964920a | |||
| 58c2e76603 | |||
| 1f8d003f7b | |||
| 5660fabb1a | |||
| 3bc1759c0e | |||
| 83f9e5519f | |||
| 50befc68f2 | |||
| a1bef582f8 | |||
| fdd0a7c6a7 | |||
| 28f85f5809 | |||
| 163f4479a2 | |||
| 92fcd08c8a | |||
| 73008ca1d9 | |||
| 80e2b55887 | |||
| 8c06b2a5f2 | |||
| 7a49f50c65 | |||
| 8597d10e1b | |||
| d6ca4e1415 | |||
| 5633d68a91 | |||
| 3b18c156f1 | |||
| 3344dabf67 | |||
| 854eee4275 |
+11
@@ -0,0 +1,11 @@
|
|||||||
|
# Build output
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
|
||||||
|
# User-specific project files
|
||||||
|
*.user
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
Kept as the work happens, not assembled at release time. Format is
|
||||||
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
||||||
|
[SemVer](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
## 1.0.0 — 2026-08-08
|
||||||
|
|
||||||
|
The first tagged version: *Outer Wilds* and *Echoes of the Eye* are translated,
|
||||||
|
which is what this mod set out to do.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Slovak localization of *Outer Wilds* and *Echoes of the Eye*, as an OWML mod
|
||||||
|
built on `xen.LocalizationUtility`.
|
||||||
|
- A fixed glossary of proper nouns and recurring terms, so names stay consistent
|
||||||
|
across the whole script.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The documentation left the repository. The working notes, the phase roadmap,
|
||||||
|
the glossary and the in-game review notes are now the
|
||||||
|
[wiki](https://git.houmeres.sk/Houmeres/Outer-Wilds-SK-Translation-Mod/wiki),
|
||||||
|
and the history was rewritten in the same pass so they are not present at any
|
||||||
|
commit. `assets/Translation.xml` stays here — it ships with the mod.
|
||||||
|
|
||||||
|
### Note on history
|
||||||
|
|
||||||
|
Any clone taken before 2026-08-08 has a different history and should be
|
||||||
|
discarded rather than merged.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using OWML.ModHelper;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// Interface copied from xen-42's Localization Utility (MIT), so this mod can
|
||||||
|
// resolve the utility's API through OWML's TryGetModApi<T>. Keep in sync with
|
||||||
|
// https://github.com/xen-42/outer-wilds-localization-utility.
|
||||||
|
namespace SlovakTranslation
|
||||||
|
{
|
||||||
|
public interface ILocalizationAPI
|
||||||
|
{
|
||||||
|
#region Add new language
|
||||||
|
void RegisterLanguage(ModBehaviour mod, string languageName, string translationPath);
|
||||||
|
void RegisterLanguage(ModBehaviour mod, string languageName, string translationPath, string languageToReplace);
|
||||||
|
void AddLanguageFont(ModBehaviour mod, string languageName, string assetBundlePath, string fontPath, out Font font);
|
||||||
|
void AddLanguageFixer(string languageName, Func<string, string> fixer);
|
||||||
|
void SetLanguageDefaultFontSpacing(string languageName, float defaultFontSpacing);
|
||||||
|
void SetLanguageFontSizeModifier(string languageName, float fontSizeModifier);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Add translations to new/existing languages
|
||||||
|
void AddTranslation(ModBehaviour mod, string languageName, string translationPath);
|
||||||
|
void AddTranslation(string languageName, KeyValuePair<string, string>[] regularEntries, KeyValuePair<string, string>[] shipLogEntries, KeyValuePair<int, string>[] uiEntries);
|
||||||
|
|
||||||
|
void AddRegularTranslation(string languageName, string key, string value);
|
||||||
|
void AddRegularTranslation(string languageName, string commonKeyPrefix, params string[] entries);
|
||||||
|
void AddRegularTranslation(string languageName, params KeyValuePair<string, string>[] entries);
|
||||||
|
|
||||||
|
void AddShiplogTranslation(string languageName, string key, string value);
|
||||||
|
void AddShiplogTranslation(string languageName, string commonKeyPrefix, params string[] entries);
|
||||||
|
void AddShiplogTranslation(string languageName, params KeyValuePair<string, string>[] entries);
|
||||||
|
|
||||||
|
void AddUITranslation(string languageName, int key, string value);
|
||||||
|
void AddUITranslation(string languageName, params KeyValuePair<int, string>[] entries);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region obsolete
|
||||||
|
[Obsolete] void AddLanguageFont(ModBehaviour mod, string languageName, string assetBundlePath, string fontPath);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,79 @@
|
|||||||
# Outer-Wilds-SK-Translation-Mod
|
# Outer Wilds — Slovenský preklad / Slovak Translation Mod
|
||||||
|
|
||||||
|
> **Neoficiálny slovenský preklad hry Outer Wilds** (základná hra + rozšírenie
|
||||||
|
> *Echoes of the Eye*). Pridáva do hry jazyk **Slovenčina**.
|
||||||
|
>
|
||||||
|
> *An unofficial Slovak (slovenčina) translation mod for Outer Wilds, covering
|
||||||
|
> the base game and the Echoes of the Eye DLC.*
|
||||||
|
|
||||||
|
**Stav / Status:** 🚧 **Work in progress** — the mod scaffold and translation are
|
||||||
|
being built. See the [Roadmap](https://git.houmeres.sk/Houmeres/Outer-Wilds-SK-Translation-Mod/wiki/Roadmap).
|
||||||
|
|
||||||
|
Outer Wilds officially ships in 12 languages; Slovak is not one of them. This mod
|
||||||
|
adds it as a new selectable language using the community localization framework —
|
||||||
|
no changes to the game's own files are required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
The mod is a small [OWML](https://github.com/ow-mods/owml) mod that registers a
|
||||||
|
new language through **[Interplanetary Polyglot / Localization Utility](https://outerwildsmods.com/mods/interplanetarypolyglot/)**
|
||||||
|
(`xen.LocalizationUtility`). All translated text lives in a single
|
||||||
|
`assets/Translation.xml`; the utility swaps the game's strings for the Slovak ones
|
||||||
|
at runtime.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install the **[Outer Wilds Mod Manager](https://outerwildsmods.com/mod-manager/)**.
|
||||||
|
2. Install this mod through the Mod Manager. Its dependency **Interplanetary
|
||||||
|
Polyglot** (`xen.LocalizationUtility`) will be pulled in automatically.
|
||||||
|
3. Launch the game via the Mod Manager.
|
||||||
|
4. In **Settings → Language**, choose **Slovenčina**.
|
||||||
|
- Tip: the optional [Language Swap](https://outerwildsmods.com/mods/languageswap/)
|
||||||
|
mod lets you switch language in-game with `Ctrl+L`.
|
||||||
|
|
||||||
|
> Until the first release is published, the mod is not yet in the Mod Manager
|
||||||
|
> database — see the [Roadmap](https://git.houmeres.sk/Houmeres/Outer-Wilds-SK-Translation-Mod/wiki/Roadmap) for how to build it from source.
|
||||||
|
|
||||||
|
## Contributing / Prispievanie
|
||||||
|
|
||||||
|
Translation fixes are very welcome — the goal is natural, consistent Slovak.
|
||||||
|
|
||||||
|
- **Report** mistranslations or awkward phrasing as an issue.
|
||||||
|
- **Edit** only the `<value>` of each entry in `assets/Translation.xml`.
|
||||||
|
**Never change the `<key>`** — it is the English identifier the game matches on.
|
||||||
|
- Preserve all inline formatting exactly, e.g. `<color=orange>…</color>`,
|
||||||
|
`<i>`, and speaker prefixes like `POKE:`.
|
||||||
|
- Follow the terminology in the [Glossary](https://git.houmeres.sk/Houmeres/Outer-Wilds-SK-Translation-Mod/wiki/Glossary) so names (Nomai, the Eye, Ash Twin
|
||||||
|
Project, …) stay consistent across the whole game.
|
||||||
|
|
||||||
|
See the [Working notes](https://git.houmeres.sk/Houmeres/Outer-Wilds-SK-Translation-Mod/wiki/Working-notes) for the full conventions and repo layout.
|
||||||
|
|
||||||
|
## License & disclaimer
|
||||||
|
|
||||||
|
The **original mod code and the Slovak translation** in this repository are
|
||||||
|
released under the [MIT License](LICENSE).
|
||||||
|
|
||||||
|
The bundled fallback font in `assets/slovakfont` is
|
||||||
|
**[Cabin](https://github.com/impallari/Cabin)** by the Cabin Project Authors, used
|
||||||
|
under the **SIL Open Font License 1.1** (see
|
||||||
|
[`assets/Cabin-OFL.txt`](assets/Cabin-OFL.txt)). The game's own menu and HUD fonts
|
||||||
|
were never baked with Slovak caron glyphs, so those labels are redrawn with a font
|
||||||
|
that has them — preferably one of the game's own dynamic fonts, and Cabin only where
|
||||||
|
it ships none. See [`SlovakFont.cs`](SlovakFont.cs), and the `preferGameTypeface`
|
||||||
|
setting if you would rather have Cabin everywhere.
|
||||||
|
|
||||||
|
This is an **unofficial** fan project. It is **not affiliated with, approved by, or
|
||||||
|
endorsed by Mobius Digital or Annapurna Interactive.** *Outer Wilds*, its story,
|
||||||
|
dialogue, characters, and other assets are **© Mobius Digital / Annapurna
|
||||||
|
Interactive**; the underlying game text remains their property and is used here
|
||||||
|
under **Mobius Digital's Fan Content Policy**. The MIT license covers only this
|
||||||
|
project's own contributions, not the source material.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- **Mobius Digital / Annapurna Interactive** — *Outer Wilds*.
|
||||||
|
- **[xen-42](https://github.com/xen-42)** — the Localization Utility /
|
||||||
|
Interplanetary Polyglot framework this mod is built on.
|
||||||
|
- The Outer Wilds modding community.
|
||||||
|
|||||||
+296
@@ -0,0 +1,296 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using OWML.Common;
|
||||||
|
using OWML.ModHelper;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace SlovakTranslation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fixes the missing Slovak caron glyphs (č ď ĺ ľ ň ŕ ť ž) in the menus and HUD.
|
||||||
|
///
|
||||||
|
/// Outer Wilds renders Latin languages with a handful of *static* font atlases,
|
||||||
|
/// and only some of them were baked with Latin Extended-A:
|
||||||
|
///
|
||||||
|
/// Adobe - SerifGothicStd (+ExtraBold) menus 253 glyphs — no carons
|
||||||
|
/// Gill Sans MT (+Menu, Bold) HUD prompts 243 glyphs — no carons
|
||||||
|
/// ParaType - Futura PT Medium dialogue 663 glyphs — complete
|
||||||
|
/// SpaceMono-Regular translator 612 glyphs — complete
|
||||||
|
///
|
||||||
|
/// That is why dialogue and the Nomai translator always looked right while
|
||||||
|
/// "Pokračovať" came out as "Pokra ova".
|
||||||
|
///
|
||||||
|
/// Registering a whole custom language font via the Localization Utility fixes
|
||||||
|
/// the menus, but it also flips <c>TextTranslation.IsLanguageLatin()</c> to
|
||||||
|
/// false, which drags dialogue, the translator and the ship log off their
|
||||||
|
/// proper fonts. And <c>UIStyleManager.GetMenuFont()</c> — the obvious hook —
|
||||||
|
/// is dead code with no callers; the fonts are assigned straight onto the
|
||||||
|
/// <see cref="Text"/> components in the scenes.
|
||||||
|
///
|
||||||
|
/// So we substitute at the Text level, but the decision is made **per font, not
|
||||||
|
/// per label**: a font that cannot draw Slovak is replaced everywhere it is
|
||||||
|
/// used, so a menu never mixes two typefaces. Fonts that can draw Slovak —
|
||||||
|
/// the dialogue and translator fonts — are left completely alone.
|
||||||
|
///
|
||||||
|
/// The replacement is preferably the game's *own* dynamic cut of the same
|
||||||
|
/// typeface (it ships several, e.g. Adobe - SerifGothicStd_Dynamic), which keeps
|
||||||
|
/// the menu pixel-identical to vanilla. Bundled Cabin is the fallback.
|
||||||
|
/// </summary>
|
||||||
|
internal static class SlovakFont
|
||||||
|
{
|
||||||
|
/// <summary>Cabin (SIL OFL 1.1), a humanist sans in the Gill Sans lineage, used
|
||||||
|
/// where the game has no dynamic cut of its own. Dynamic, so it can draw any
|
||||||
|
/// glyph Slovak needs.</summary>
|
||||||
|
private static Font _font;
|
||||||
|
|
||||||
|
/// <summary>The letters Slovak adds on top of plain ASCII. Only these are worth
|
||||||
|
/// probing — anything else in the text was already fine in English.</summary>
|
||||||
|
private const string SlovakLetters = "áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ";
|
||||||
|
|
||||||
|
/// <summary>Stylised display faces (the ship's readouts, seven-segment digits).
|
||||||
|
/// Replacing these wholesale would cost more in looks than it gains, so they
|
||||||
|
/// are only substituted on the individual labels that actually need a glyph.</summary>
|
||||||
|
private static readonly string[] Stylised = { "VCR_OSD_MONO", "digital-7", "PCBius" };
|
||||||
|
|
||||||
|
/// <summary>Font instance ID → the Slovak letters that font cannot draw.</summary>
|
||||||
|
private static readonly Dictionary<int, string> _gaps = new Dictionary<int, string>();
|
||||||
|
|
||||||
|
/// <summary>Labels we substituted, and the font the game wanted on them.</summary>
|
||||||
|
private static readonly Dictionary<Text, Font> _swapped = new Dictionary<Text, Font>();
|
||||||
|
|
||||||
|
/// <summary>Original font instance ID → what we put in its place.</summary>
|
||||||
|
private static readonly Dictionary<int, Font> _substitutes = new Dictionary<int, Font>();
|
||||||
|
|
||||||
|
/// <summary>The game's own dynamic fonts, which can draw glyphs their static
|
||||||
|
/// siblings were never baked with.</summary>
|
||||||
|
private static Font[] _gameDynamicFonts = new Font[0];
|
||||||
|
|
||||||
|
/// <summary>When set, a static font is replaced by the game's own dynamic cut of the
|
||||||
|
/// same typeface where one exists, keeping the vanilla look. Cabin otherwise.</summary>
|
||||||
|
internal static bool PreferGameTypeface = true;
|
||||||
|
|
||||||
|
private static ModBehaviour _mod;
|
||||||
|
|
||||||
|
internal static bool Loaded => _font != null;
|
||||||
|
|
||||||
|
internal static void Load(ModBehaviour mod)
|
||||||
|
{
|
||||||
|
_mod = mod;
|
||||||
|
var path = mod.ModHelper.Manifest.ModFolderPath + "assets/slovakfont";
|
||||||
|
var bundle = AssetBundle.LoadFromFile(path);
|
||||||
|
if (bundle == null)
|
||||||
|
{
|
||||||
|
mod.ModHelper.Console.WriteLine("Could not load font bundle at " + path, MessageType.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var font in bundle.LoadAllAssets<Font>())
|
||||||
|
{
|
||||||
|
if (font.name != "SlovakUI") continue;
|
||||||
|
_font = font;
|
||||||
|
Object.DontDestroyOnLoad(font);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_font == null)
|
||||||
|
mod.ModHelper.Console.WriteLine("Font bundle has no 'SlovakUI' font.", MessageType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Give <paramref name="text"/> a font that can draw Slovak, or hand back
|
||||||
|
/// the game's own font once it is no longer needed.</summary>
|
||||||
|
internal static void Apply(Text text)
|
||||||
|
{
|
||||||
|
if (_font == null || text == null) return;
|
||||||
|
|
||||||
|
var current = text.font;
|
||||||
|
Font stored;
|
||||||
|
var swapped = _swapped.TryGetValue(text, out stored) && stored != null &&
|
||||||
|
current == SubstituteFor(stored);
|
||||||
|
|
||||||
|
// If anything else assigned a font since our swap, that is the new baseline.
|
||||||
|
var wanted = swapped ? stored : current;
|
||||||
|
if (!swapped) _swapped.Remove(text);
|
||||||
|
if (wanted == null) return;
|
||||||
|
|
||||||
|
var needsHelp = NeedsSubstitute(wanted, text.text);
|
||||||
|
if (needsHelp == swapped) return;
|
||||||
|
|
||||||
|
if (needsHelp)
|
||||||
|
{
|
||||||
|
_swapped[text] = wanted;
|
||||||
|
text.font = SubstituteFor(wanted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_swapped.Remove(text);
|
||||||
|
text.font = wanted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>What to draw <paramref name="original"/>'s labels with instead. The
|
||||||
|
/// game ships dynamic cuts of several of its typefaces; borrowing the matching one
|
||||||
|
/// keeps the menu looking exactly like vanilla, which no substitute font can.</summary>
|
||||||
|
private static Font SubstituteFor(Font original)
|
||||||
|
{
|
||||||
|
Font cached;
|
||||||
|
if (_substitutes.TryGetValue(original.GetInstanceID(), out cached) && cached != null) return cached;
|
||||||
|
if (!PreferGameTypeface) return _font;
|
||||||
|
|
||||||
|
if (_gameDynamicFonts.Length == 0) RefreshGameFonts();
|
||||||
|
|
||||||
|
Font best = null;
|
||||||
|
var bestLength = 0;
|
||||||
|
foreach (var candidate in _gameDynamicFonts)
|
||||||
|
{
|
||||||
|
if (candidate == null) continue;
|
||||||
|
var baseName = candidate.name.EndsWith("_Dynamic")
|
||||||
|
? candidate.name.Substring(0, candidate.name.Length - "_Dynamic".Length)
|
||||||
|
: candidate.name;
|
||||||
|
|
||||||
|
if (baseName.Length <= bestLength) continue;
|
||||||
|
if (!original.name.StartsWith(baseName, System.StringComparison.Ordinal)) continue;
|
||||||
|
if (GapsOf(candidate).Length != 0) continue;
|
||||||
|
|
||||||
|
best = candidate;
|
||||||
|
bestLength = baseName.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best == null) return _font;
|
||||||
|
|
||||||
|
_substitutes[original.GetInstanceID()] = best;
|
||||||
|
_mod?.ModHelper.Console.WriteLine(
|
||||||
|
$"drawing \"{original.name}\" with the game's own \"{best.name}\"", MessageType.Info);
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshGameFonts()
|
||||||
|
{
|
||||||
|
var found = new List<Font>();
|
||||||
|
foreach (var font in Resources.FindObjectsOfTypeAll<Font>())
|
||||||
|
{
|
||||||
|
if (font != null && font.dynamic && font != _font) found.Add(font);
|
||||||
|
}
|
||||||
|
_gameDynamicFonts = found.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Drop every cached decision, so a settings change takes effect at once.</summary>
|
||||||
|
internal static void ForgetChoices()
|
||||||
|
{
|
||||||
|
_substitutes.Clear();
|
||||||
|
_gameDynamicFonts = new Font[0];
|
||||||
|
foreach (var pair in _swapped)
|
||||||
|
{
|
||||||
|
if (pair.Key != null && pair.Value != null) pair.Key.font = pair.Value;
|
||||||
|
}
|
||||||
|
_swapped.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Re-check every live label. Scene loads and language changes reassign
|
||||||
|
/// fonts wholesale, and menus are built lazily, so sweep periodically too.</summary>
|
||||||
|
internal static void ApplyToAll()
|
||||||
|
{
|
||||||
|
if (_font == null) return;
|
||||||
|
|
||||||
|
// Labels that have since been destroyed would otherwise pile up.
|
||||||
|
var dead = new List<Text>();
|
||||||
|
foreach (var pair in _swapped)
|
||||||
|
{
|
||||||
|
if (pair.Key == null) dead.Add(pair.Key);
|
||||||
|
}
|
||||||
|
foreach (var text in dead) _swapped.Remove(text);
|
||||||
|
|
||||||
|
RefreshGameFonts();
|
||||||
|
foreach (var text in Object.FindObjectsOfType<Text>()) Apply(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool NeedsSubstitute(Font font, string content)
|
||||||
|
{
|
||||||
|
if (!CustomLanguageActive()) return false;
|
||||||
|
|
||||||
|
var gaps = GapsOf(font);
|
||||||
|
if (gaps.Length == 0) return false;
|
||||||
|
|
||||||
|
// Ordinary UI faces are replaced everywhere they appear, so a menu never
|
||||||
|
// mixes typefaces. Stylised readouts only give way when a glyph is at stake.
|
||||||
|
if (!IsStylised(font.name)) return true;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(content)) return false;
|
||||||
|
foreach (var c in content)
|
||||||
|
{
|
||||||
|
if (gaps.IndexOf(c) >= 0) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GapsOf(Font font)
|
||||||
|
{
|
||||||
|
var id = font.GetInstanceID();
|
||||||
|
string gaps;
|
||||||
|
if (_gaps.TryGetValue(id, out gaps)) return gaps;
|
||||||
|
|
||||||
|
// Dynamic fonts only report a character as present once it has been
|
||||||
|
// rasterised, so ask for the whole Slovak set before probing.
|
||||||
|
if (font.dynamic) font.RequestCharactersInTexture(SlovakLetters);
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
foreach (var c in SlovakLetters)
|
||||||
|
{
|
||||||
|
if (!font.HasCharacter(c)) builder.Append(c);
|
||||||
|
}
|
||||||
|
gaps = builder.ToString();
|
||||||
|
_gaps[id] = gaps;
|
||||||
|
|
||||||
|
if (gaps.Length > 0)
|
||||||
|
{
|
||||||
|
_mod?.ModHelper.Console.WriteLine(
|
||||||
|
$"font \"{font.name}\" cannot draw \"{gaps}\" — " +
|
||||||
|
(IsStylised(font.name) ? "substituting only where needed" : "substituting everywhere"),
|
||||||
|
MessageType.Info);
|
||||||
|
}
|
||||||
|
return gaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsStylised(string fontName)
|
||||||
|
{
|
||||||
|
foreach (var stylised in Stylised)
|
||||||
|
{
|
||||||
|
if (fontName.IndexOf(stylised, System.StringComparison.OrdinalIgnoreCase) >= 0) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>True while a mod-registered language is selected. The Localization
|
||||||
|
/// Utility appends custom languages past <c>Language.TOTAL</c>, and treats
|
||||||
|
/// everything up to it as vanilla — same test it uses itself.</summary>
|
||||||
|
private static bool CustomLanguageActive()
|
||||||
|
{
|
||||||
|
if (TextTranslation.s_theTable == null) return false;
|
||||||
|
return TextTranslation.Get().GetLanguage() > TextTranslation.Language.TOTAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hooks: every label as it appears or changes, plus a sweep whenever the game
|
||||||
|
/// reassigns fonts wholesale.
|
||||||
|
/// </summary>
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal static class SlovakFontPatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix, HarmonyPatch(typeof(Text), "OnEnable")]
|
||||||
|
public static void TextEnabled(Text __instance) => SlovakFont.Apply(__instance);
|
||||||
|
|
||||||
|
[HarmonyPostfix, HarmonyPatch(typeof(Text), "text", MethodType.Setter)]
|
||||||
|
public static void TextChanged(Text __instance) => SlovakFont.Apply(__instance);
|
||||||
|
|
||||||
|
// The game reassigns fonts wholesale when the language changes. Re-deciding here
|
||||||
|
// is safe: our own assignment re-enters once, finds the state already correct,
|
||||||
|
// and stops.
|
||||||
|
[HarmonyPostfix, HarmonyPatch(typeof(Text), "font", MethodType.Setter)]
|
||||||
|
public static void FontAssigned(Text __instance) => SlovakFont.Apply(__instance);
|
||||||
|
|
||||||
|
[HarmonyPostfix, HarmonyPatch(typeof(TextTranslation), nameof(TextTranslation.SetLanguage))]
|
||||||
|
public static void LanguageChanged() => SlovakFont.ApplyToAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using OWML.Common;
|
||||||
|
using OWML.ModHelper;
|
||||||
|
|
||||||
|
namespace SlovakTranslation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers Slovenčina as a selectable language via xen-42's Localization
|
||||||
|
/// Utility (xen.LocalizationUtility). All the heavy lifting lives in the
|
||||||
|
/// utility; this mod just points it at our translated strings and patches the
|
||||||
|
/// few menu/HUD fonts that were never baked with Slovak glyphs (see
|
||||||
|
/// <see cref="SlovakFont"/>).
|
||||||
|
/// </summary>
|
||||||
|
public class SlovakTranslation : ModBehaviour
|
||||||
|
{
|
||||||
|
public static SlovakTranslation Instance;
|
||||||
|
|
||||||
|
/// <summary>Name the language is registered (and displayed) under.</summary>
|
||||||
|
public const string LanguageName = "Slovenčina";
|
||||||
|
|
||||||
|
/// <summary>Path (relative to the mod folder) to the translated strings.</summary>
|
||||||
|
public static string translationFile = "assets/Translation.xml";
|
||||||
|
|
||||||
|
/// <summary>The Localization Utility's API, kept for reflection in <see cref="SlovakFont"/>.</summary>
|
||||||
|
internal static ILocalizationAPI LocalizationApi;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
|
LocalizationApi = ModHelper.Interaction.TryGetModApi<ILocalizationAPI>("xen.LocalizationUtility");
|
||||||
|
if (LocalizationApi == null)
|
||||||
|
{
|
||||||
|
ModHelper.Console.WriteLine(
|
||||||
|
"Could not find xen.LocalizationUtility — is Interplanetary Polyglot installed?",
|
||||||
|
MessageType.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizationApi.RegisterLanguage(this, LanguageName, translationFile);
|
||||||
|
ModHelper.Console.WriteLine("Registered language: " + LanguageName, MessageType.Success);
|
||||||
|
|
||||||
|
// Deliberately *not* AddLanguageFont: that would make IsLanguageLatin false
|
||||||
|
// and pull dialogue, the translator and the ship log off their correct
|
||||||
|
// vanilla fonts. We only patch the fonts that can't draw Slovak.
|
||||||
|
SlovakFont.Load(this);
|
||||||
|
if (SlovakFont.Loaded)
|
||||||
|
{
|
||||||
|
new Harmony("Homer.SlovakTranslation").PatchAll();
|
||||||
|
UnityEngine.SceneManagement.SceneManager.sceneLoaded += (scene, mode) => SlovakFont.ApplyToAll();
|
||||||
|
ModHelper.Console.WriteLine("Slovak menu/HUD font (Cabin) loaded and patched in.", MessageType.Success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <c>preferGameTypeface</c> — when on, a menu font that can't draw Slovak is
|
||||||
|
/// replaced by the game's own dynamic cut of the same typeface where one exists,
|
||||||
|
/// so the menu keeps its vanilla look. Turn it off to use the bundled Cabin
|
||||||
|
/// everywhere instead.
|
||||||
|
/// </summary>
|
||||||
|
public override void Configure(IModConfig config)
|
||||||
|
{
|
||||||
|
SlovakFont.PreferGameTypeface = config.GetSettingsValue<bool>("preferGameTypeface");
|
||||||
|
if (!SlovakFont.Loaded) return;
|
||||||
|
|
||||||
|
SlovakFont.ForgetChoices();
|
||||||
|
SlovakFont.ApplyToAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||||
|
<RootNamespace>SlovakTranslation</RootNamespace>
|
||||||
|
<AssemblyName>SlovakTranslation</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Lets net48 build on machines without the .NET Framework targeting pack
|
||||||
|
(e.g. Linux / dotnet SDK only). Build-time only. -->
|
||||||
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="HarmonyX" Version="2.10.0" />
|
||||||
|
<PackageReference Include="OWML" Version="2.5.2" />
|
||||||
|
<!-- Provides the Outer Wilds / Unity reference DLLs from NuGet, so no game
|
||||||
|
install is needed to compile. -->
|
||||||
|
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.168" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="manifest.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="default-config.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="assets/Translation.xml">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="assets/slovakfont">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30114.105
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlovakTranslation", "SlovakTranslation.csproj", "{227A7B9F-9B05-4AE7-BDB9-1DE031C15853}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{227A7B9F-9B05-4AE7-BDB9-1DE031C15853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{227A7B9F-9B05-4AE7-BDB9-1DE031C15853}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{227A7B9F-9B05-4AE7-BDB9-1DE031C15853}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{227A7B9F-9B05-4AE7-BDB9-1DE031C15853}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2018 The Cabin Project Authors (https://github.com/impallari/Cabin.git)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
+16268
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"settings": {
|
||||||
|
"preferGameTypeface": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"filename": "SlovakTranslation.dll",
|
||||||
|
"author": "Jaroslav Beneš",
|
||||||
|
"name": "Slovak Localization / Slovenčina",
|
||||||
|
"uniqueName": "Homer.SlovakTranslation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"owmlVersion": "2.5.2",
|
||||||
|
"dependencies": [
|
||||||
|
"xen.LocalizationUtility"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user