Yarn Spinner is a tool for writers. In this section, you'll learn the syntax of Yarn, and learn how to write Yarn scripts for use in your game. You'll also learn how to use the various tools that are available for creating your content.
You'll need an editor before you can write Yarn scripts, so we recommend checking out before going too far.
if statementsIn addition to storing information, variables are useful for controlling what's shown to the player. To do this, you use if statements.
An if statement allows you to control whether a collection of content is shown or not. When you write an if statement, you provide an expression, which is checked; if that expression evaluates to a "true" value, then all of the content in between the <<if>> and <<endif>> statements are run.
For example, consider the following code:
<<set $gold_amount to 5>>
Player: I'd like to buy a pie!
<<if $gold_amount < 10>>
Baker: Well, you can't afford one!
<<endif>>
This example will set a variable, $gold_amount, to 5. It will then show the line "I'd like to buy a pie!", and before it continues, it will check to see if $gold_amount is less than 10. If that's the case (which it will be!), the line "Well, you can't afford one!" will run.
You can use the elseif and else statements to handle different situations in an if statement.
An elseif statement has an expression that gets checked if the if statement, or any previous elseif statements, don't run.
An else statement doesn't have an expression, and runs if the if and any elseif don't run.
For example:
This script will show different lines depending on the value of $gold_amount. The checks are done from top to bottom, which means that in order for an elseif or else to run, all of the checks above it have to have failed.
If it's less than 10, the line "Well, you can't afford one!" will run.
Otherwise, if it's less than 15, the line "You can almost afford one!" will run.
Otherwise, the line "Here you go!" will run.
When presenting options to the player, you may want to make some options not available. You can do this by adding a condition to the option.
For example, if you have a variable that tracks your player's "reputation points", called $reputation, you might want to make certain options only available if the value of $reputation is high enough.
Conditions on options are done by adding an if statement to the end of the option. They look like this:
When Yarn Spinner runs this collection of options, it will check the expression inside the if statement. If the expression is false, then the option will be marked as unavailable.
Now that you know how to work with , , and , there's one last part of the Yarn language to learn about: commands.
In Yarn Spinner, all of your dialogue is stored in .yarn files. Yarn files are just plain text files, which you can edit in any text editor.
Yarn Spinner files contain nodes. Nodes are where you put your dialogue. You can have as many nodes as you link in a file. Nodes are used to separate out parts of the story, and make it easier to manage longer stories and branching.
Each node has, at the very minimum, a collection of headers, and a body. All nodes have at least one header, which is the title. The title is the name of the node, and the body contains the Yarn script that contains your game's dialogue.
The title of a node is important, because your game uses node titles to tell Yarn Spinner which node to start running. You also use the title of a node when you want to jump to another node.
Node titles are not shown to the player.
If you're using a text editor to write Yarn scripts, you'll need to write the node's header.
Player: I'd like to buy a pie!
<<if $gold_amount < 10>>
Baker: Well, you can't afford one!
<<elseif $gold_amount < 15>>
Baker: You can almost afford one!
<<else>>
Baker: Here you go!
<<endif>>Guard: You're not allowed in!
-> Sure I am! The boss knows me! <<if $reputation > 10>>
-> Please?The plain-text version of a Yarn node looks like this:
In this example, the node's title is Node_Title, which is set on the first line in the title header. You can also add any other headers that you want.
The --- marker indicates where the body begins. After this point, you can put all of your Yarn script.
The === marker indicates where the node ends; after this point, you can begin another node.
The body of a node is made up of three different kinds of content: lines, commands, and options.
When you write Yarn Spinner dialogue, just about every line of text that you write in a node is a line. When a node is run, it runs each line, one at a time, and sends it to your game.
A line of dialogue is just the thing you want some entity or character to say, usually beginning with the name of the entity speaking.
For example, consider the following Yarn code from Night in the Woods:
When this code is run in the game, it looks like this:
Yarn Spinner sends each of these lines, one at a time, to the game. The game is responsible for taking the text, and presenting it to the player; in the case of Night in the Woods, this means drawing the speech bubble, animating each letter in, and waiting for the user to press a key to advance to the next line.
Lines of dialogue can contain just about any text, except for some special characters that Yarn Spinner uses to add extra information to a line.
If there is a set of characters without spaces before a colon (:) at the beginning of the line, Yarn Spinner will mark that as the name of the character. This information will then be passed to your game, so that you can change the way that lines are shown based on the character who's saying them. For example:
When you want to let the player decide what to say, you use an option. Options let you show multiple potential lines of dialogue to the player, and let the player select one.
Options are marked with a -> symbol. You write as many options as you'd like the player to see, and the player chooses one of them. The content of the option is like any other line of dialogue.
For example, consider the following code:
In this example, the line "Hi there! What do you feel like doing today?" will run. The player will then be given the choice to say either "I want to go swimming", or "I'd prefer to go hiking".
Shortcut options can have their own lines, which are run when the option is selected. If a different option is selected, they won't run. To write this, indent the lines that belong to a shortcut option.
In the following code, different lines will run based on which of the two shortcut options are selected.
This script will start with the line, "Hi there! What do you feel like doing today?". The player then has the choice of saying either "I want to go swimming", or "I'd prefer to go hiking". Depending on their choice, either the line "Okay, let's go swimming" or "Cool, we'll go hiking then". Finally, no matter what was selected, the line "Sounds good!" will run.
In addition to containing lines, options can also contain other options.
You can nest options as much as you like. However, this can get a bit challenging to read. It's often a good idea to use the <<jump>> command to jump to a different node:
Separating dialogue segments into nodes can aid in writing neater files that are easier to edit as they grow.
Sometimes it makes sense for the options presented or the outcomes of selecting different options to vary based on other things the player has done or said up until this point. This requires the use of logic and variables, which we'll discuss in the next section.
Node titles must start with a letter, and can contain letters, numbers and underscores.
So FirstNode, First_Node and Node1 valid, but First Node and 1stNode are not.
Node names cannot contain a . (period).Node names were able to contain a period in Yarn Spinner 1, and if your Yarn Spinner 1 .yarn scripts have periods in the node names, you can use the to convert them (and all jumps and options related) to use a _ (underscore) instead.
title: Node_Title
---
Here are some lines!
Wow!
===Mae: Well, this is great.
Mae: I mean I didn't expect a party or anything
Mae: but I figured *someone* would be here.
Mae: ...
Mae: Welcome home, Mae.This is a line of dialogue, without a character name.
Speaker: This is another line of dialogue said by a character called "Speaker".Companion: Hi there! What do you feel like doing today?
-> Player: I want to go swimming.
-> Player: I'd prefer to go hiking.Companion: Hi there! What do you feel like doing today?
-> Player: I want to go swimming.
Companion: Okay, let's go swimming.
-> Player: I'd prefer to go hiking.
Companion: Cool, we'll go hiking then.
Player: Sounds good!Companion: Hi there! What do you feel like doing today?
-> Player: I want to go swimming.
Companion: Okay, let's go swimming.
Companion: Where do you want to swim?
-> Player: The lake!
Companion: Nice! It's a great day for it.
-> Player: The swimming pool!
Companion: Oh, awesome! I heard they installed a new slide.
-> Player: I'd prefer to go hiking.
Companion: Cool, we'll go hiking then.
Player: Sounds good!title: Start
---
Companion: Hi there! What do you feel like doing today?
-> Player: I want to go swimming.
Companion: Okay, let's go swimming.
Companion: Where do you want to swim?
-> Player: The lake!
Companion: Nice! It's a great day for it.
-> Player: The swimming pool!
Companion: Oh, awesome! I heard they installed a new slide.
-> Player: I'd prefer to go hiking.
Companion: Cool, we'll go hiking then.
Player: Sounds good!
===title: Start
---
Companion: Hi there! What do you feel like doing today?
-> Player: I want to go swimming.
Companion: Okay, let's go swimming.
<<jump Swimming>>
-> Player: I'd prefer to go hiking.
Companion: Cool, we'll go hiking then.
<<jump Hiking>>
===
title: Swimming
---
Companion: Where do you want to swim?
-> Player: The lake!
Companion: Nice! It's a great day for it.
-> Player: The swimming pool!
Companion: Oh, awesome! I heard they installed a new slide.
<<jump Done>>
===
title: Hiking
---
Companion: Have you got your hiking boots ready?
-> Player: Yes.
Companion: Great, let's go!
-> Player: No.
Companion: We can swing by your place and pick them up!
<<jump Done>>
===
title: Done
---
Player: Sounds good!
===
A function is a block of code that provides a value to your Yarn scripts, which you can use in if statements, or store in variables.
In Yarn Spinner scripts, functions perform two main kinds of task:
Functions let you get values that change over time, or that depend on other values. For example, the random function returns a different random number every time you call it.
Functions let you get data from your game back into your scripts.
You call a function inside an expression. For example:
Yarn Spinner comes with several built-in functions for you to use.
visited returns a boolean value of true if the node with the title of node_name has been entered and exited at least once before, otherwise returns false. Will return false if node_name doesn't match a node in project.
visted_count returns a number value of the number of times the node with the title of node_name has been entered and exited, otherwise returns 0. Will return 0 if node_name doesn't match a node in project.
format_invariant returns a string representation of n, formatted using the invariant culture. This is useful for embedding numbers in commands, where the command expects the number to be formatted using the invariant culture. For example, <<give_gold {$gold}>>, which might end up as give_gold 4,51 in German, but give_gold 4.51 in English, can now be <<give_gold {format_invariant($gold)}>>, which will always be give_gold 4.51.
format is fuctionally identical to the C# string.Format, but only accepts one parameter. For example, format("{0}", 123) will return the String "123", and format("${0:F2}", 0.451) will return the String "$0.45". You can .
random returns a random number between 0 and 1 each time you call it.
random_range returns a random number between a and b, inclusive.
dice returns a random integer between 1 and sides, inclusive.
For example, dice(6) returns a number between 1 and 6, just like rolling a six-sided die.
round rounds n to the nearest integer.
round_places rounds n to the nearest number with places decimal points.
floor rounds n down to the nearest integer, towards negative infinity.
ceil rounds n up to the nearest integer, towards positive infinity.
inc rounds n up to the nearest integer. If n is already an integer, inc returns n+1.
dec rounds n down to the nearest integer. If n is already an integer, dec returns n-1.
decimal returns the decimal portion of n. This will always be a number between 0 and 1. For example, decimal(4.51) will return 0.51.
int rounds n down to the nearest integer, towards zero.
You can define your own custom functions in Yarn Spinner. For more information, see .
The Yarn language is a full programming language, which means it has support for writing code that let you control how the dialogue in your game works. In this section, you'll learn how to use variables to control your dialogue.
Variables store information. Variables can store one of three types of information: numbers, strings, and booleans.
In Yarn Spinner, you can send instructions to your game through commands. Commands look like this:
Commands are sent to your game's , just like lines and options are. Commands are not shown to the player directly; instead, they're used for things like stage directions.
Yarn Spinner comes with some built-in commands; however, to get the most usefulness out of them, you'll want to that make your game do what you need to.
There are two built-in commands in Yarn Spinner: wait, and stop.
The wait command pauses the dialogue for a specified number of seconds, and then resumes. You can use integers (whole numbers), or decimals.
Due to a bug, in versions of Yarn Spinner prior to version 3.0, this function returns a number between 0 and sides - 1, inclusive.
Functions are not intended to be a way for you to send instructions to your game. For that purpose, you should use commands.
In particular, functions are not guaranteed to be called in the same order as they appear in your code, or even be called at all if Yarn Spinner believes the result can be cached. As much as possible, custom functions should be pure functions, and have no side effects besides returning a value based on parameters.
stop command immediately ends the dialogue, as though the game had reached the end of a node. Use this if you need to leave a conversation in the middle of an if statement, or a shortcut option.You can create your own commands, so that your scripts can send directions to your game. For more information on how to create them in Unity games, see Creating Commands and Functions.
// Inside an if statement:
<<if dice(6) == 6>>
You rolled a six!
<<endif>>
// Inside a line:
Gambler: My lucky number is {random_range(1,10)}!<<wait 2>>
<<setsprite ShipName happy>>
<<fade_out 1.5>>// Wait for 2 seconds
<<wait 2>>
// Wait for half a second
<<wait 0.5>>// Leave the dialogue now
<<stop>>
// Leave the dialogue if we don't have enough money
<<if $money < 50>>
Shopkeeper: You can't afford my pies!
<<stop>>
<<endif>>Number
Any whole or decimal number
1, 2.5, 3468900, -500
String
Any sequence of letters, numbers and other characters, enclosed in quotes.
"Hello", "✓", "A whole sentence."
Boolean
Either the value true or the value false.
true, false
Every variable has a name. In Yarn Spinner, all variable names start with a dollar sign ($).
Declaring a variable means telling Yarn Spinner that a variable exists, what it's meant to be used for, and what initial value it has.
To declare a variable, you use the <<declare>> statement:
You put information into a variable by using the <<set>> command. For example, the following code puts a string, "Hello, Yarn!", into a variable called $greeting:
Each variable can only store one type of value. Variables can change their value at any time, but they can never change their type.
For example, the following code will work:
This works because while the value of each of the variable changes, the type doesn't. However, the following code will not work:
You can work with the values inside variables. For example, numbers can be multiplied, strings can be added together, and boolean values can have logical operations (like and and or) applied to them. When values are used together like this, it's called an expression.
An expression needs to be a single type. You can't work with values of different types in a single expression. For example, the following code will not work:
Yarn Spinner supports the following logical operators. Most of these have multiple ways being written:
Equality: eq or is or ==
Inequality: neq or !
Greater than: gt or >
Less than: lt or <
Less than or equal to: lte or <=
Greater than or equal to: gte or >=
Boolean 'or'': or or ||
Boolean 'xor': xor or ^
Boolean 'not': not or !
Boolean 'and': and or &&
Addition: +
Subtraction: -
Multiplication: *
Division: /
Truncating Remainder Division: %
Brackets: ( to open the brackets and ) to close them.
Yarn Spinner follows a fairly standard order of operations, and falls back to using left to right when operators are of equivalent priority.
The order of operations is as follows:
Brackets
Boolean Negation
Multiplication, Division, and Truncating Remainder Division
Addition, Subtraction
Less than or equals, Greater than or equals, Less than, Greater than
Equality, Inequality
Boolean AND, Boolean OR, Boolean XOR
To show the contents of a variable, you put it inside braces ({ }) inside a line. The value of that variable will appear in its place.
For example:
<<set $variableName to "a string value">>
The value of variableName is {$variableName}.The value of variableName is a string value.Yarn Spinner doesn’t manage the storage of information in variables itself. Instead, your game provides a variable storage object to Yarn Spinner before you start running dialogue.
When Yarn Spinner needs to know the value of a variable, it will ask the variable storage object you’ve given it. When Yarn Spinner wants to set the value of a variable, it will provide the value and the name of the variable. In this way, your game has control over how data is stored.
The specifics of how variables need to be stored will vary depending on what game engine you're using Yarn Spinner in. To learn more about variable storage in Unity, see Variable Storage.
/// The name of the player.
<<declare $playerName = "Player">>
/// The number of gold pieces that the player has.
<<declare $gold = 0>>
/// Is the door to the dungeon unlocked?
<<declare $doorUnlocked = false>><<set $greeting to "Hello, Yarn!">>// Set some initial values in some variables
<<set $myCoolNumber to 7>>
<<set $myFantasticString to "wow, text!">>
// Now change them!
<<set $myCoolNumber to 8>>
<<set $myFantasticString to "incredible!">>// Set some initial values in some variables
<<set $myCoolNumber to 7>>
<<set $myFantasticString to "wow, text!">>
// This will NOT work, because you can't change types!
<<set $myCoolNumber to "8">>
<<set $myFantasticString to 42>><<set $variableName to "a string value">><<set $numberOfSidesInATriangle = 2 + 1>>
<<set $numberOfSidesInASquare = $numberOfSidesInATriangle + 1>>// This will NOT work, because you can't add a string and a number:
<<set $broken = "hello" + 1>>If you use a variable without declaring it, Yarn Spinner will try to figure out what type it should have based on how it's being used in your scripts, as well as what initial value it should have - zero for numbers, false for booleans, and blank text for strings. When a variable is not declared, we call that an implicit declaration.
If you declare a variable, you can make sure that the type of the variable is what you intend it to be. Declaring a variable also lets you control what the variable's initial value is, and lets you add descriptive comments that explain the purpose of the variable to other people (or to your future self!)
As with node titles, variable names must not contain spaces. While they can contain a range of different characters the first character must be a letter. In general your variables will be made up of only letters, numbers and underscores.
A tag is a piece of information that you can add to content in Yarn Spinner that adds additional context or detail about that content.
There are two places you can add tags in Yarn scripts: you can add them to nodes, and you can add them to lines. Tags aren't shown to the user; instead, they're used by your game, or by Yarn Spinner itself.
Tags can be added to the end of lines and options. Tags on lines start with a hash symbol (#), and cannot contain spaces. You can add as many tags as you like to line, but they must all be on the same line in the script.
Here's an example of a line with two tags:
Homer: Hi, I'd like to order a tire balancing. #tone:sarcastic #duplicateTags that you add to a line can be accessed from your game. The way that you access them depends on your game engine. For example, to access them in a Unity game, you use the LocalizedLine.Metadata property.
Some tags are used by Yarn Spinner itself, while all others are used by your own code, so it's up to you what content they should have, and how to handle them.
Certain tags are used internally by Yarn Spinner, or are added for you if they don't exist.
The Yarn Spinner compiler adds a #lastline tag to every line that comes just before a set of options.
For example, the following excerpt:
is treated as though it had been written as:
In a Unity game, you can use this tag in a custom to be notified ahead of time when the player is about to be shown options.
The #line tag uniquely identifies a single line or option across all of your game's dialogue. This is used to identify lines for localisation. Every line's #line tag must be unique. If a line or option doesn't have a #line tag, it will be automatically added for you.
Here's an example of some Yarn script with #line tags:
For more details (including what the tag should look like), see .
Nodes can also have tags, which you can use to add labels that describe the node.
Node tags they work a bit differently than line tags: they are defined in the header with the tags key, and they don't have to begin with a hash symbol (#).
Here's an example of a node with two tags:
You can access the tags on a node via the Dialogue object's method.
The metadata of a line is only composed of tags. Because of this, you may find that the Yarn Spinner code and documentation refer to line tags and line metadata interchangeably.
Nodes can have other metadata in their headers. This metadata isn't exposed through the API, which means it's mostly used to store additional information for whoever is writing the Yarn dialogue or for editors to make use of.
However, currently there is one header that defines specific behavior within the Yarn Spinner compiler: the tracking header.
Nodes can track whether they have already been visited during the game. For this to work, the Yarn Spinner compiler needs to add some special code to the node. To avoid creating this code for nodes that don't need it, the compiler only adds this code if it finds a call to the visited() function with the node name in it.
In some cases, you may need the compiler to add this special code to a node even if no corresponding visited() call exists. To direct the compiler to do this, include the tracking header with the value of always:
Additionally, using a value of never instructs the compiler to never add this special code to the node. If you use the visited function with a node set to never use tracking, it will always return false.
For more information on visit tracking, see the documentation for .
Tags and metadata may seem very complicated at first, and their uses may not be clear. The following example use cases explain how they can be used in your game. Keep in mind that this is not an exhaustive list of use cases.
Yarn provides to let you change attributes for specific parts of a line. In case most of your attributes apply to entire lines (for example, the color of a line), it may be easier to just use tags instead.
The #lastline tag can be used to display the last line of dialogue along with any options. This is handled within your code by checking if a line has the #lastline tag, and if it does, storing it before continuing with the execution of the Yarn dialogue.
Since metadata isn't shown directly to the player, you can use metadata for any internal workflows or tooling. For example, instead of tracking lines that need to be revised outside the Yarn files (which could lead to syncing problems), you could add line tags (such as #needsrevision) to the appropriate lines directly in the Yarn files, and process these lines as part of an internal tool or workflow. The Unity integration automatically generates a CSV file with all lines that contain metadata, making this super easy!
As referenced before, the Yarn Spinner integration for Unity uses line tags to link localised dialogue lines. This is better explained in the section.
Aside from that, every piece of metadata can be used by translators and adapters to help them understand how the text is being used, thus leading to better localised text.
Some games may require that certain lines of dialogue are displayed somewhere other than the dialogue window (for example, as flavor text for an item description, or in an item that acts as a log). Instead of manually duplicating these lines (which adds overhead during development and localisation), tags can be used along with code that checks for the tags and duplicates the lines while the game is running.
Hello there.
-> Hi!
-> What's up?Hello there. #lastline
-> Hi!
-> What's up?Mechanic: You're in orbit of Jupiter, at a rest station along the main tourism lines. There's a meteorite headed towards here that'll completely destroy this station in three days. #line:4c49c5
Mechanic: And you're a wayfinding robot bolted to the floor of said Jupiter Tourist Station. #line:5b6256
-> Bolted to the floor?! #line:f65d07
Mechanic: Yeah, like all tourist helper bots. #line:1b159b
-> Three days?! #line:40eaf7
Mechanic: More or less. I wouldn't make any long-term plans. #line:3a6c94title: Train_Dialogue
tags: #camera2 background:conductor_cabin
---
Why did you stop the train?
Now we won't arrive in time at the next stop!
===title: Node_Name
tracking: always
---
I know how many times you've been here.
===The bool function converts values of any type into a boolean value (if it can be interpreted as one.)
/// What day number it is. Starts on day 0, ends on day 3.
<<declare $day = 0 as number>>
Markup allows you to add attributes into your text, like [a]hello[/a]. These attributes can be used by your game to do things like change the formatting of the text, add animations, and more.
When text is parsed, the tags are removed from the text, and you receive information about the range of the plain text that the attributes apply to.
Attributes apply to ranges of text:
Oh, [wave]hello[/wave] there!Yarn Spinner will take this text, and produce two things: the plain text, and a collection of attributes. The plain text is the text without any markers; in this example it will be:
Oh, hello there!Attributes represent ranges of the plain text that have additional information. They contain a position, a length, and their name, as well as their properties.
In this example, a single attribute will be generated, with a position of 4, a length of 5, and a name of "wave".
Attributes are opened like [this], and closed like [/this].
Attributes can overlap:
You can put multiple attributes inside each other. For example:
You can close an attribute in any order you like. For example, this has the same meaning as the previous example:
Attributes can self-close:
A self-closing attribute has a length of zero.
The marker [/] is the close-all marker. It closes all currently open attributes. For example:
Attributes can have properties:
This attribute 'wave' has a property called 'size', which has an integer value of 2.
Attributes can have short-hand properies, like so:
This is the same as saying this:
This attribute 'wave' has a property called 'wave', which has an integer value of 2. The name of the attribute is taken from the first property.
Properties can be any of the following types:
Integers
Floats
'true' or 'false'
Strings
Single words without quote marks are parsed as strings. For example, the two following lines are identical:
If a self-closing attribute has white-space before it, or it's at the start of the line, then it will trim a single whitespace after it. This means that the following text produces a plain text of "A B":
You may want to show text containing the [ and ] characters to your player. To prevent the markup parser from treating as special characters, you can escape them. Text that has been escaped will be treated as plain text, and will not be interpreted by the parser.
There are two ways to escape your markup: escaping single characters, and using the nomarkup attribute.
If you need to escape a single square bracket character, put a backslash \ in front of it:
This will appear to the player as:
The backslash will not appear in the text.
If you want to escape a longer run of text, or if you have many square brackets, escaping a single character at a time can be cumbersome. In these cases, you may want to escape an entire region of text, using the nomarkup attribute. This attribute makes the parser ignore any markup characters inside it.
If you want to include characters like [ and ], wrap them in the nomarkup attribute:
This will appear as:
The character attribute is used to mark the part of the line that identifies the character that's speaking.
Yarn Spinner will attempt to add this character for you, by looking for character names in lines that look like this:
The markup parser will mark everything from the start of the line up to the first : (and any trailing whitespace after it) with the character attribute. This attribute has a property, name, which contains the text from the start of the line up to the :. If a : isn't present, or a character attribute has been added in markup, it won't be added.
This means that the example above is treated the same as this:
You can use this to trim out the character names from lines in your game.
Certain attributes in Yarn Spinner's markup are "replacement" markers, which Yarn Spinner uses to insert or replace text based on the value of a variable. There are three built-in replacement markers:
The select marker uses the value of a variable to choose an outcome.
The plural marker uses the value of a number to decide on the plural class for that number.
The ordinal marker uses the value of a number to decide on the
All three of these markers have a property called value, and use this to decide what text should be used in the line.
The select marker is the simplest of the built-in replacement markers. It takes the value of the value property, and uses that to choose a replacement.
It's especially useful for when you need to insert a gendered pronoun in a line:
The plural and ordinal markers take a number in its value property, and use that to determine the plural or ordinal number class of that value.
Different languages have different rules for how numbers are pluralised.
In many languages, the term you use to refer to a thing depends on the the number of that thing. This is known as a plural class: in English, you can have one apple, but many apples, and you have have one mouse, but many mice.
However, the rules vary significantly across different languages. English has two: "single", and "other". However, for example, Polish has multiple.
In English, you say "one apple, two apples, five apples".
In Polish, you say "jedno jabłko, dwa jabłka, pięć jabłek".
Notice how the Polish word for "apple", "jabłko", takes multiple forms as the number changes, whereas it takes two forms in English.
In Yarn Spinner, individual lines are replaced depending on the user's locale, but the logic surround them is not. This means that, if you want to be able to translate your game into multiple languages, you can't write Yarn code like this:
If you did it this way, the logic would only work for languages that have the same rules for plurals as English. (There are several of them that do, but far more that don't.)
Complicating this further, there are two main kinds of plural classes: cardinal plural classes, and ordinal plural classes.
Cardinal plural classes are the kind we just saw (for example, "one apple, two apples").
Ordinal plural classes refer to the positioning of a thing; in English, ordinal numbers are things like "1st, 2nd, 3rd."
As with cardinal plural classes, different languages have different ordinal plural classes.
Yarn Spinner is able to take a number and the user's current locale, and determine the correct cardinal or ordinal plural class of that number, for that locale. You can then use the plural class to decide on what text to show.
plural and ordinal have a property called value, just like select. They then have a property for each of the current locale's plural classes. These can be:
one
two
few
The two markers differ based on what kind of plural class they work with:
plural selects a number's cardinal plural class.
ordinal selects a number's ordinal plural class.
Not every language uses every category; for example, English only uses "one" and "other" for cardinal plural classes.
For each of these properties, you provide the text that should appear.
For example:
You can include the actual value in the resulting text by using the % character. This character will be replaced with the value provided to the value property:
The ordinal marker works similarly, but uses the ordinal plural class:
many
other
Oh, [wave]hello [bounce]there![/bounce][/wave]Oh, [wave]hello [bounce]there![/wave][/bounce][wave/][wave][bounce]Hello![/][wave size=2]Wavy![/wave][wave=2]Wavy![/wave][wave wave=2]Wavy![/wave][mood=angry]Grr![/mood]
[mood="angry"]Grr![/mood]A [wave/] BHere's some square brackets, just for you: \[ \]Here's some square brackets, just for you: [ ][nomarkup]Here's a big ol' [ bunch of ] characters, filled [[]] with square [[] brackets![/nomarkup]Here's a big ol' [ bunch of ] characters, filled [[]] with square [[] brackets!CharacterA: Hello!
CharacterB: Oh, hi![character name="CharacterA"]CharacterA: [/character]Hello!
[character name="CharacterB"]CharacterB: [/character]Oh hi!// In this example, the $gender variable is a string that
// contains either "m", "f", or "nb".
I think [select value={$gender} m="he" f="she" nb="they" /] will be there!
// Depending on the value of $gender, this line can appear
// as one of these possible options:
I think he will be there!
// or:
I think she will be there!
// or:
I think they will be there!<<if $apple_count == 1>>
You have one apple!
<<else>>
You have {$apple_count} apples!
<<endif>>PieMaker: Hey, look! [plural value={$pie_count} one="A pie" other="Some pies" /]!
// This will appear as either:
PieMaker: Hey, look! A pie!
// or:
PieMaker: Hey, look! Some pies!PieMaker: I just baked [plural value={$pie_count} one="a pie" other="% pies" /]!
// This will appear as, for example:
PieMaker: I just baked a pie!"
// or:
PieMaker: I just baked 4 pies!"Runner: The race is over! I came in [ordinal value={$race_position} one="%st" two="%nd" few="%rd" other="%th" /] place!
// This will appear as, for example:
Runner: The race is over! I came in 1st place!
// or:
Runner: The race is over! I came in 23rd place!A [wave trimwhitespace=false/] B
// (produces "A B")Here's a backslash! \\Here's a backslash! \