This commit is contained in:
SoulliesOfficial
2026-06-09 11:21:59 -04:00
parent 7c60c40d6b
commit 021e76efe7
493 changed files with 50500 additions and 2211 deletions

View File

@@ -0,0 +1,188 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
#nullable enable
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Yarn.Unity.Attributes;
#nullable enable
namespace Yarn.Unity
{
public sealed class BuiltinLocalisedLineProvider : LineProviderBehaviour, ILineProvider
{
public override string LocaleCode
{
get => _textLocaleCode;
set => _textLocaleCode = value;
}
[SerializeField, Language] private string _textLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
[SerializeField, Language] private string _assetLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
[SerializeField] private bool _useFallback = false;
[ShowIf(nameof(_useFallback))]
[SerializeField, Language] private string _fallbackLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
public string AssetLocaleCode
{
get => _assetLocaleCode;
set => _assetLocaleCode = value;
}
private Markup.LineParser lineParser = new Markup.LineParser();
private Markup.BuiltInMarkupReplacer builtInReplacer = new Markup.BuiltInMarkupReplacer();
public override void RegisterMarkerProcessor(string attributeName, Markup.IAttributeMarkerProcessor markerProcessor)
{
lineParser.RegisterMarkerProcessor(attributeName, markerProcessor);
}
public override void DeregisterMarkerProcessor(string attributeName)
{
lineParser.DeregisterMarkerProcessor(attributeName);
}
void Awake()
{
lineParser.RegisterMarkerProcessor("select", builtInReplacer);
lineParser.RegisterMarkerProcessor("plural", builtInReplacer);
lineParser.RegisterMarkerProcessor("ordinal", builtInReplacer);
}
public override async YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken)
{
string sourceLineID = line.ID;
string[] metadata = System.Array.Empty<string>();
// Check to see if this line shadows another. If it does, we'll use
// that line's text and asset.
if (YarnProject != null)
{
metadata = YarnProject.lineMetadata?.GetMetadata(line.ID) ?? System.Array.Empty<string>();
var shadowLineSource = YarnProject.lineMetadata?.GetShadowLineSource(line.ID);
if (shadowLineSource != null)
{
sourceLineID = shadowLineSource;
}
}
string? text = GetLocalizedString(sourceLineID);
Object? asset = await GetLocalizedAssetAsync(sourceLineID);
if (text == null)
{
// No line available.
Debug.LogWarning($"Localization {LocaleCode} does not contain an entry for line {line.ID}", this);
return LocalizedLine.InvalidLine;
}
var parseResult = lineParser.ParseString(Markup.LineParser.ExpandSubstitutions(text, line.Substitutions), LocaleCode);
return new LocalizedLine
{
Text = parseResult,
RawText = text,
TextID = line.ID,
Asset = asset,
Metadata = metadata,
};
}
private Localization GetLocalization(string locale)
{
if (YarnProject == null)
{
throw new System.InvalidOperationException($"Can't get localized line: no Yarn Project set");
}
Localization loc = YarnProject.GetLocalization(locale);
if (loc == null)
{
throw new System.InvalidOperationException($"Can't get localized line: Yarn Project has no localisation for {locale}");
}
return loc;
}
private string? GetLocalizedString(string sourceLineID)
{
var baseLoc = GetLocalization(_textLocaleCode);
string? text = baseLoc.GetLocalizedString(sourceLineID);
if (text != null)
{
return text;
}
if (_useFallback)
{
var fallbackLoc = GetLocalization(_fallbackLocaleCode);
return fallbackLoc.GetLocalizedString(sourceLineID);
}
return null;
}
private async YarnTask<Object?> GetLocalizedAssetAsync(string sourceLineID)
{
var baseLoc = GetLocalization(_assetLocaleCode);
Object? result = await baseLoc.GetLocalizedObjectAsync<Object>(sourceLineID);
if (result != null)
{
return result;
}
if (_useFallback)
{
var fallbackLoc = GetLocalization(_fallbackLocaleCode);
return await fallbackLoc.GetLocalizedObjectAsync<Object>(sourceLineID);
}
return null;
}
public async override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
{
if (YarnProject == null)
{
// We don't have a Yarn Project, so there's no preparation we
// can do. do.
return;
}
var assetLocalization = YarnProject.GetLocalization(AssetLocaleCode);
if (assetLocalization.UsesAddressableAssets)
{
// The localization uses addressable assets. Ensure that these
// assets are pre-loaded.
var tasks = new List<YarnTask<Object?>>();
foreach (var id in lineIDs)
{
var task = assetLocalization.GetLocalizedObjectAsync<Object>(id);
tasks.Add(task);
}
await YarnTask.WhenAll(tasks);
}
else
{
// The localization uses direct references. No need to pre-load
// the assets - they were loaded with the scene.
return;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b6798449caec4a32a5ad27badb06624
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using System.Collections.Generic;
#nullable enable
namespace Yarn.Unity
{
/// <summary>
/// Contains methods for accessing assets of a given type stored within an
/// object.
/// </summary>
public interface IAssetProvider
{
/// <summary>
/// Attempts to fetch an asset of type <typeparamref name="T"/> from the
/// object.
/// </summary>
/// <typeparam name="T">The type of the assets.</typeparam>
/// <param name="result">On return, the fetched asset, or <see
/// langword="null"/>.</param>
/// <returns><see langword="true"/> if an asset was fetched; <see
/// langword="false"/> otherwise.</returns>
public bool TryGetAsset<T>([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? result) where T : UnityEngine.Object;
/// <summary>
/// Gets a collection of assets of type <typeparamref name="T"/> from
/// the target.
/// </summary>
/// <typeparam name="T">The type of the asset.</typeparam>
/// <returns>A collection of assets. This collection may be
/// empty.</returns>
public IEnumerable<T> GetAssetsOfType<T>() where T : UnityEngine.Object;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e028f35c1f4a4b06bc175cf06aa72c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,132 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Yarn.Markup;
#nullable enable
namespace Yarn.Unity
{
/// <summary>
/// Contains methods for retrieving user-facing localized content, given
/// non-localized line IDs.
/// </summary>
public interface ILineProvider
{
/// <summary>
/// The <see cref="YarnProject"/> that contains the localized data for
/// lines.
/// </summary>
/// <remarks>
/// This property is set at run-time by the object that will be requesting
/// content (typically a <see cref="DialogueRunner"/>).
/// </remarks>
public YarnProject? YarnProject { get; set; }
/// <summary>
/// Gets the line provider's current locale identifier, as a BCP-47 code.
/// </summary>
public string LocaleCode { get; }
/// <summary>
/// Prepares and returns a <see cref="LocalizedLine"/> from the specified
/// <see cref="Yarn.Line"/>.
/// </summary>
/// <param name="line">The <see cref="Yarn.Line"/> to produce the <see
/// cref="LocalizedLine"/> from.</param>
/// <param name="cancellationToken">A cancellation token that indicates
/// whether the process of fetching the localised version of <paramref
/// name="line"/> should be cancelled.</param>
/// <returns>A localized line, ready to be presented to the
/// player.</returns>
public YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken);
/// <summary>
/// Signals to the line provider that lines with the provided line IDs may
/// be presented shortly.
/// </summary>
/// <remarks>
/// <para>
/// This method allows implementing classes a chance to prepare any
/// neccessary resources needed to present these lines, like pre-loading
/// voice-over audio. The default implementation does nothing.
/// </para>
/// <para style="info">
/// Not every line may run; this method serves as a way to give the line
/// provider advance notice that a line <i>may</i> run, not <i>will</i> run.
/// </para>
/// </remarks>
/// <param name="lineIDs">A collection of line IDs that the line provider
/// should prepare for.</param>
/// <param name="cancellationToken">A cancellation token that indicates
/// whether the operation should be cancelled.</param>
public YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken);
/// <summary>
/// Adds a new marker processor to the line provider.
/// </summary>
/// <param name="attributeName">The name of the markers to use <paramref
/// name="markerProcessor"/> for.</param>
/// <param name="markerProcessor">The marker processor to add.</param>
public void RegisterMarkerProcessor(string attributeName, Yarn.Markup.IAttributeMarkerProcessor markerProcessor);
/// <summary>
/// Removes all marker processors that handle markers named <paramref
/// name="attributeName"/>.
/// </summary>
/// <param name="attributeName">The name of the marker to remove processors
/// for.</param>
public void DeregisterMarkerProcessor(string attributeName);
}
/// <summary>
/// A <see cref="MonoBehaviour"/> that produces <see
/// cref="LocalizedLine"/>s, for use in Dialogue Presenters.
/// </summary>
/// <remarks>
/// <see cref="DialogueRunner"/>s use a <see cref="LineProviderBehaviour"/>
/// to get <see cref="LocalizedLine"/>s, which contain the localized
/// information that is presented to the player through dialogue presenters.
/// </remarks>
public abstract class LineProviderBehaviour : MonoBehaviour, ILineProvider
{
/// <inheritdoc/>
public abstract YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken);
/// <inheritdoc/>
public YarnProject? YarnProject { get; set; }
/// <inheritdoc/>
public virtual YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
{
// No-op by default.
return YarnTask.CompletedTask;
}
/// <inheritdoc/>
public abstract string LocaleCode { get; set; }
/// <summary>
/// Called by Unity when the <see cref="LineProviderBehaviour"/> has
/// first appeared in the scene.
/// </summary>
/// <remarks>
/// This method is <see langword="public"/> <see langword="virtual"/> to
/// allow subclasses to override it.
/// </remarks>
public virtual void Start()
{
}
/// <inheritdoc/>
public abstract void RegisterMarkerProcessor(string attributeName, IAttributeMarkerProcessor markerProcessor);
/// <inheritdoc/>
public abstract void DeregisterMarkerProcessor(string attributeName);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1724d534cf292400da295a39a3a8d97c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,125 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using UnityEngine;
#nullable enable
namespace Yarn.Unity
{
/// <summary>
/// Represents a line, ready to be presented to the user in the localisation
/// they have specified.
/// </summary>
public class LocalizedLine
{
/// <summary>
/// DialogueLine's ID
/// </summary>
public string TextID = "<unknown>";
/// <summary>
/// DialogueLine's inline expression's substitution
/// </summary>
public string[] Substitutions = System.Array.Empty<string>();
/// <summary>
/// DialogueLine's text
/// </summary>
public string? RawText;
/// <summary>
/// Any metadata associated with this line.
/// </summary>
public string[] Metadata = System.Array.Empty<string>();
/// <summary>
/// The name of the character, if present.
/// </summary>
/// <remarks>
/// This value will be <see langword="null"/> if the line does not have
/// a character name.
/// </remarks>
public string? CharacterName
{
get
{
if (Text.TryGetAttributeWithName("character", out var characterNameAttribute))
{
if (characterNameAttribute.Properties.TryGetValue("name", out var value))
{
return value.StringValue;
}
}
return null;
}
}
/// <summary>
/// The asset associated with this line, if any.
/// </summary>
public Object? Asset;
/// <summary>
/// The object that created this line.
/// Most of the time will be the <see cref="DialogueRunner"/> that passed the presenter the line.
/// </summary>
/// <remarks>
/// This exists for situations where you need the dialogue runner (or your custom equivalent) to send back messages.
/// In particular this is used by the <see cref="VoiceOverPresenter"/> to get a reference to the dialogue runner to advance lines after playback is finished without needing a specific reference.
/// Allowing the presenter to be reused across multiple runners.
/// </remarks>
public object? Source;
/// <summary>
/// The underlying <see cref="Yarn.Markup.MarkupParseResult"/> for this
/// line.
/// </summary>
public Markup.MarkupParseResult Text { get; set; }
/// <summary>
/// The underlying <see cref="Yarn.Markup.MarkupParseResult"/> for this
/// line, with any `character` attribute removed.
/// </summary>
/// <remarks>
/// If the line has no `character` attribute, this method returns the
/// same value as <see cref="Text"/>.
/// </remarks>
public Markup.MarkupParseResult TextWithoutCharacterName
{
get
{
// If a 'character' attribute is present, remove its text
if (Text.TryGetAttributeWithName("character", out var characterNameAttribute))
{
// because of how we delete the text we also clear up the attributes
// most of the time this is the right play
// however the character feels important enough to add it back in
var characterless = Text.DeleteRange(characterNameAttribute);
characterless.Attributes.Add(characterNameAttribute);
return characterless;
}
else
{
return Text;
}
}
}
/// <summary>
/// A <see cref="LocalizedLine"/> object that represents content not
/// being found.
/// </summary>
public static readonly LocalizedLine InvalidLine = new LocalizedLine
{
Asset = null,
Metadata = System.Array.Empty<string>(),
RawText = "!! ERROR: Missing line!",
Substitutions = System.Array.Empty<string>(),
TextID = "<missing>",
Text = new Markup.MarkupParseResult("!! ERROR: Missing line!", new System.Collections.Generic.List<Markup.MarkupAttribute>())
};
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5fca305f6aaeb461381b51f8f37c31b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,228 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
#if USE_UNITY_LOCALIZATION
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Metadata;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.ResourceManagement.Exceptions;
using Yarn.Markup;
#nullable enable
namespace Yarn.Unity.UnityLocalization
{
/// <summary>
/// Contains Yarn Spinner related metadata for Unity string table entries.
/// </summary>
[System.Serializable]
public class LineMetadata : IMetadata
{
/// <summary>
/// The name of the Yarn node that this line came from.
/// </summary>
public string nodeName = "";
/// <summary>
/// The <c>#hashtags</c> present on the line.
/// </summary>
public string[] tags = System.Array.Empty<string>();
/// <summary>
/// Gets the line ID indicated by any shadow tag contained in this
/// metadata, if present.
/// </summary>
public string? ShadowLineSource
{
get
{
foreach (var metadataEntry in tags)
{
if (metadataEntry.StartsWith("shadow:"))
{
// This is a shadow line. Return the line ID that it's
// shadowing.
return "line:" + metadataEntry.Substring("shadow:".Length);
}
}
// The line had metadata, but it wasn't a shadow line.
return null;
}
}
}
/// <summary>
/// A line provider that uses the Unity Localization system to get localized
/// content for Yarn lines.
/// </summary>
public partial class UnityLocalisedLineProvider : LineProviderBehaviour
{
// the string table asset that has all of our (hopefully) localised
// strings inside
[SerializeField] internal LocalizedStringTable? stringsTable;
[SerializeField] internal LocalizedAssetTable? assetTable;
/// <inheritdoc/>
public override string LocaleCode
{
get => LocalizationSettings.SelectedLocale.Identifier.Code;
set
{
Locale? locale = LocalizationSettings.AvailableLocales.GetLocale(value);
if (locale == null)
{
throw new System.InvalidOperationException($"Can't set locale to {value}: no such locale has been configured");
}
LocalizationSettings.SelectedLocale = locale;
}
}
private LineParser lineParser = new LineParser();
private BuiltInMarkupReplacer builtInReplacer = new BuiltInMarkupReplacer();
void Awake()
{
lineParser.RegisterMarkerProcessor("select", builtInReplacer);
lineParser.RegisterMarkerProcessor("plural", builtInReplacer);
lineParser.RegisterMarkerProcessor("ordinal", builtInReplacer);
}
/// <inheritdoc/>
public override async YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken)
{
if (stringsTable == null || stringsTable.IsEmpty)
{
throw new System.InvalidOperationException($"Tried to get localised line for {line.ID}, but no string table has been set.");
}
var getStringOp = LocalizationSettings.StringDatabase.GetTableEntryAsync(stringsTable.TableReference, line.ID, null, FallbackBehavior.UseFallback);
var entry = await YarnTask.WaitForAsyncOperation(getStringOp, cancellationToken);
// Attempt to fetch metadata tags for this line from the string
// table
var metadata = entry.Entry?.SharedEntry.Metadata.GetMetadata<LineMetadata>();
// Get the text from the entry
var text = entry.Entry?.LocalizedValue
?? $"!! Error: Missing localisation for line {line.ID} in string table {entry.Table.LocaleIdentifier}";
string? shadowLineID = metadata?.ShadowLineSource;
if (shadowLineID != null)
{
// This line actually shadows another line. Fetch that line, and
// use its text (but not its metadata)
var getShadowLineOp = LocalizationSettings.StringDatabase.GetTableEntryAsync(stringsTable.TableReference, shadowLineID, null, FallbackBehavior.UseFallback);
var shadowEntry = await YarnTask.WaitForAsyncOperation(getShadowLineOp, cancellationToken);
if (shadowEntry.Entry == null)
{
Debug.LogWarning($"Line {line.ID} shadows line {shadowLineID}, but no such entry was found in the string table {stringsTable.TableReference}");
}
else
{
text = shadowEntry.Entry.LocalizedValue;
}
}
// We now have our text; parse it as markup
var markup = lineParser.ParseString(LineParser.ExpandSubstitutions(text, line.Substitutions), this.LocaleCode);
// Lastly, attempt to fetch an asset for this line
Object? asset = null;
if (this.assetTable != null && this.assetTable.IsEmpty == false)
{
// Fetch the asset for this line, if one is available.
var loadOp = LocalizationSettings.AssetDatabase.GetLocalizedAssetAsync<Object>(assetTable.TableReference, shadowLineID ?? line.ID, null, FallbackBehavior.UseFallback);
asset = await YarnTask.WaitForAsyncOperation(loadOp, cancellationToken);
}
// Construct the localized line
LocalizedLine localizedLine = new LocalizedLine()
{
Text = markup,
TextID = line.ID,
Substitutions = line.Substitutions,
RawText = text,
Metadata = metadata?.tags ?? System.Array.Empty<string>(),
Asset = asset,
};
return localizedLine;
}
/// <inheritdoc/>
public override void RegisterMarkerProcessor(string attributeName, IAttributeMarkerProcessor markerProcessor)
{
lineParser.RegisterMarkerProcessor(attributeName, markerProcessor);
}
/// <inheritdoc/>
public override void DeregisterMarkerProcessor(string attributeName)
{
lineParser.DeregisterMarkerProcessor(attributeName);
}
/// <inheritdoc/>
public override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
{
// Nothing to do. If a user wants to ensure ahead of time that
// localized content is already in memory, they should use the Unity
// Localization preload support.
return YarnTask.CompletedTask;
}
}
}
#if UNITY_EDITOR
namespace Yarn.Unity.UnityLocalization.Editor
{
using System;
using UnityEditor;
[CustomEditor(typeof(UnityLocalisedLineProvider))]
internal class UnityLocalisedLineProviderEditor : UnityEditor.Editor
{
private SerializedProperty? stringsTableProperty;
private SerializedProperty? assetTableProperty;
/// <summary>
/// Called by Unity to draw the inspector GUI.
/// </summary>
public override void OnInspectorGUI()
{
EditorGUILayout.PropertyField(stringsTableProperty);
var stringTableName = stringsTableProperty?.FindPropertyRelative("m_TableReference").FindPropertyRelative("m_TableCollectionName").stringValue;
if (string.IsNullOrEmpty(stringTableName))
{
EditorGUI.indentLevel += 1;
EditorGUILayout.HelpBox("Choose a strings table to make this line provider able to deliver line text.", MessageType.Warning);
EditorGUI.indentLevel -= 1;
}
EditorGUILayout.PropertyField(assetTableProperty);
serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// Called by Unity when the editor is enabled to set up cached properties.
/// </summary>
protected void OnEnable()
{
this.stringsTableProperty = serializedObject.FindProperty(nameof(UnityLocalisedLineProvider.stringsTable));
this.assetTableProperty = serializedObject.FindProperty(nameof(UnityLocalisedLineProvider.assetTable));
}
}
}
#endif // UNITY_EDITOR
#endif // USE_UNITY_LOCALIZATION

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d23c0c44d94eb467780430dc536b6ea1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,101 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
#if !USE_UNITY_LOCALIZATION
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
#nullable enable
namespace Yarn.Unity.UnityLocalization
{
/// <summary>
/// A line provider that uses the Unity Localization system to get localized
/// content for Yarn lines.
/// </summary>
public partial class UnityLocalisedLineProvider : LineProviderBehaviour
{
// When Unity Localization is not installed, types like TableReference
// no longer exist, and can't be deserialized into. This causes a loss
// of data. To get around this, we declare a new type with the same
// shape as TableReference to keep the data in. If and when Unity
// Localization is added to the project, the data stored in these fields
// is deserialized into actual TableReferences.
[System.Serializable]
public struct PlaceholderTableReference
{
[System.Serializable]
public struct PlaceholderTableIdentifier
{
[SerializeField] private string m_TableCollectionName;
}
[SerializeField] private PlaceholderTableIdentifier m_TableReference;
}
[SerializeField] internal PlaceholderTableReference? stringsTable;
[SerializeField] internal PlaceholderTableReference? assetTable;
/// <inheritdoc/>
public override string LocaleCode { get => "error"; set { } }
private const string NotInstalledError = nameof(UnityLocalisedLineProvider) + "requires that the Unity Localization package is installed in the project. To fix this, install Unity Localization.";
/// <inheritdoc/>
public override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
{
Debug.LogError(NotInstalledError);
return YarnTask.CompletedTask;
}
/// <inheritdoc/>
public override void Start()
{
Debug.LogError(NotInstalledError);
}
/// <inheritdoc/>
public override YarnTask<LocalizedLine> GetLocalizedLineAsync(Yarn.Line line, CancellationToken cancellationToken)
{
Debug.LogError($"{nameof(UnityLocalisedLineProvider)}: Can't create a localised line for ID {line.ID} because the Unity Localization package is not installed in this project. To fix this, install Unity Localization.");
return YarnTask.FromResult(new LocalizedLine()
{
TextID = line.ID,
RawText = $"{line.ID}: Unable to create a localised line, because the Unity Localization package is not installed in this project.",
Substitutions = line.Substitutions,
});
}
/// <inheritdoc/>
public override void RegisterMarkerProcessor(string attributeName, Markup.IAttributeMarkerProcessor markerProcessor)
{
Debug.LogWarning($"Unable to add a marker processor for {attributeName}, as the Unity Localization package is not installed in this project");
}
/// <inheritdoc/>
public override void DeregisterMarkerProcessor(string attributeName)
{
Debug.LogWarning($"Unable to remove a marker processor for {attributeName}, as the Unity Localization package is not installed in this project");
}
}
#if UNITY_EDITOR
namespace Editor
{
using UnityEditor;
[CustomEditor(typeof(UnityLocalisedLineProvider))]
public class UnityLocalisedLineProviderPlaceholderEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("Unity Localization is not installed.", MessageType.Warning);
}
}
}
#endif
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0dcd24d82774e4ea1b71ab83bdf69c3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
#nullable enable
namespace Yarn.Unity.UnityLocalization
{
// This partial declaration exists to ensure that Unity is able to work with
// this class, because MonoBehaviour types are required to be defined in a
// file whose name matches the type's name. The actual definition of
// UnityLocalisedLineProvider is found in either
// UnityLocalisedLineProvider.Installed.cs (if Unity Localization is
// installed), or UnityLocalisedLineProvider.NotInstalled.cs (if not).
public sealed partial class UnityLocalisedLineProvider : LineProviderBehaviour { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9c1f487b798d402db42bc11cee5b810
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: