3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-10 22:19:31 +01:00

Update translation framework, add badge to readme

This commit is contained in:
Daniel Oaks 2018-01-23 15:06:55 +10:00
parent 1d22d65f9f
commit 75dcff7183
9 changed files with 332 additions and 173 deletions

View File

@ -10,6 +10,7 @@ Oragono is a fork of the [Ergonomadic](https://github.com/edmund-huber/ergonomad
[![Build Status](https://travis-ci.org/oragono/oragono.svg?branch=master)](https://travis-ci.org/oragono/oragono)
[![Download Latest Release](https://img.shields.io/badge/downloads-latest%20release-green.svg)](https://github.com/oragono/oragono/releases/latest)
[![Freenode #oragono](https://img.shields.io/badge/Freenode-%23oragono-1e72ff.svg?style=flat)](https://www.irccloud.com/invite?channel=%23oragono&hostname=irc.freenode.net&port=6697&ssl=1)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/oragono/localized.svg)](https://crowdin.com/project/oragono)
[darwin.network](https://irc.darwin.network/) is running Oragono in production if you want to take a look.

View File

@ -7,6 +7,7 @@ package irc
import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
@ -496,16 +497,18 @@ func LoadConfig(filename string) (config *Config, err error) {
continue
}
// only load .lang.yaml files
// only load core .lang.yaml files, and ignore help/irc files
name := f.Name()
if !strings.HasSuffix(strings.ToLower(name), ".lang.yaml") {
lowerName := strings.ToLower(name)
if !strings.HasSuffix(lowerName, ".lang.yaml") {
continue
}
// don't load our example file in practice
if strings.ToLower(name) == "example.lang.yaml" {
// don't load our example files in practice
if strings.HasPrefix(lowerName, "example") {
continue
}
// load core info file
data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, name))
if err != nil {
return nil, fmt.Errorf("Could not load language file [%s]: %s", name, err.Error())
@ -516,6 +519,52 @@ func LoadConfig(filename string) (config *Config, err error) {
if err != nil {
return nil, fmt.Errorf("Could not parse language file [%s]: %s", name, err.Error())
}
langInfo.Translations = make(map[string]string)
// load actual translation files
var tlList map[string]string
// load irc strings file
ircName := strings.TrimSuffix(name, ".lang.yaml") + "-irc.lang.json"
data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, ircName))
if err != nil {
return nil, fmt.Errorf("Could not load language's irc file [%s]: %s", ircName, err.Error())
}
err = json.Unmarshal(data, &tlList)
if err != nil {
return nil, fmt.Errorf("Could not parse language's irc file [%s]: %s", ircName, err.Error())
}
for key, value := range tlList {
// because of how crowdin works, this is how we skip untranslated lines
if key == value {
continue
}
langInfo.Translations[key] = value
}
// load help strings file
helpName := strings.TrimSuffix(name, ".lang.yaml") + "-help.lang.json"
data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, helpName))
if err != nil {
return nil, fmt.Errorf("Could not load language's help file [%s]: %s", helpName, err.Error())
}
err = json.Unmarshal(data, &tlList)
if err != nil {
return nil, fmt.Errorf("Could not parse language's help file [%s]: %s", helpName, err.Error())
}
for key, value := range tlList {
// because of how crowdin works, this is how we skip untranslated lines
if key == value {
continue
}
langInfo.Translations[key] = value
}
// confirm that values are correct
if langInfo.Code == "en" {
@ -527,7 +576,7 @@ func LoadConfig(filename string) (config *Config, err error) {
}
if len(langInfo.Translations) == 0 {
return nil, fmt.Errorf("Language file [%s] contains no translations", name)
return nil, fmt.Errorf("Language [%s / %s] contains no translations", langInfo.Code, langInfo.Name)
}
// check for duplicate languages

View File

@ -372,10 +372,10 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
var snoDescription string
if durationIsUsed {
client.Notice(fmt.Sprintf("Added temporary (%s) D-Line for %s", duration.String(), hostString))
client.Notice(fmt.Sprintf(client.t("Added temporary (%s) D-Line for %s"), duration.String(), hostString))
snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added temporary (%s) D-Line for %s"), client.nick, operName, duration.String(), hostString)
} else {
client.Notice(fmt.Sprintf("Added D-Line for %s", hostString))
client.Notice(fmt.Sprintf(client.t("Added D-Line for %s"), hostString))
snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added D-Line for %s"), client.nick, operName, hostString)
}
server.snomasks.Send(sno.LocalXline, snoDescription)

View File

@ -34,7 +34,15 @@ func NewLanguageManager(defaultLang string, languageData map[string]LangData) *L
// load language data
for name, data := range languageData {
lm.Info[name] = data
lm.translations[name] = data.Translations
// make sure we don't include empty translations
lm.translations[name] = make(map[string]string)
for key, value := range data.Translations {
if strings.TrimSpace(value) == "" {
continue
}
lm.translations[name][key] = value
}
}
return &lm

View File

@ -2235,7 +2235,7 @@ func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
}
var (
infoString = strings.Split(` ·
infoString1 = strings.Split(` ·
·
. .
@ -2243,17 +2243,11 @@ var (
https://oragono.io/
https://github.com/oragono/oragono
Oragono is released under the MIT license.
Thanks to Jeremy Latt for founding Ergonomadic, the project this is based on <3
Core Developers:
Daniel Oakley, DanielOaks, <daniel@danieloaks.net>
`, "\n")
infoString2 = strings.Split(` Daniel Oakley, DanielOaks, <daniel@danieloaks.net>
Shivaram Lingamneni, slingamn, <slingamn@cs.stanford.edu>
Contributors and Former Developers:
3onyc
`, "\n")
infoString3 = strings.Split(` 3onyc
Edmund Huber
Euan Kemp (euank)
Jeremy Latt
@ -2267,7 +2261,21 @@ Contributors and Former Developers:
)
func infoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
for _, line := range infoString {
// we do the below so that the human-readable lines in info can be translated.
for _, line := range infoString1 {
client.Send(nil, server.name, RPL_INFO, client.nick, line)
}
client.Send(nil, server.name, RPL_INFO, client.nick, client.t("Oragono is released under the MIT license."))
client.Send(nil, server.name, RPL_INFO, client.nick, "")
client.Send(nil, server.name, RPL_INFO, client.nick, client.t("Thanks to Jeremy Latt for founding Ergonomadic, the project this is based on <3"))
client.Send(nil, server.name, RPL_INFO, client.nick, "")
client.Send(nil, server.name, RPL_INFO, client.nick, client.t("Core Developers:"))
for _, line := range infoString2 {
client.Send(nil, server.name, RPL_INFO, client.nick, line)
}
client.Send(nil, server.name, RPL_INFO, client.nick, "")
client.Send(nil, server.name, RPL_INFO, client.nick, client.t("Contributors and Former Developers:"))
for _, line := range infoString3 {
client.Send(nil, server.name, RPL_INFO, client.nick, line)
}
client.Send(nil, server.name, RPL_ENDOFINFO, client.nick, "End of /INFO")

View File

@ -0,0 +1,65 @@
{
"= Help Topics =\n\nCommands:\n%s\n\nRPL_ISUPPORT Tokens:\n%s\n\nInformation:\n%s": "= Help Topics =\n\nCommands:\n%s\n\nRPL_ISUPPORT Tokens:\n%s\n\nInformation:\n%s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +r | Only registered users can talk in the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +r | Only registered users can talk in the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html",
"ACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "ACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.",
"AUTHENTICATE\n\nUsed during SASL authentication. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/extensions/sasl-3.1.html": "AUTHENTICATE\n\nUsed during SASL authentication. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/extensions/sasl-3.1.html",
"AWAY [message]\n\nIf [message] is sent, marks you away. If [message] is not sent, marks you no\nlonger away.": "AWAY [message]\n\nIf [message] is sent, marks you away. If [message] is not sent, marks you no\nlonger away.",
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html",
"CHANSERV <subcommand> [params]\n\nChanServ controls channel registrations.": "CHANSERV <subcommand> [params]\n\nChanServ controls channel registrations.",
"CS <subcommand> [params]\n\nChanServ controls channel registrations.": "CS <subcommand> [params]\n\nChanServ controls channel registrations.",
"DEBUG <option>\n\nPrints debug information about the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes out the CPU profiler info.": "DEBUG <option>\n\nPrints debug information about the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes out the CPU profiler info.",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.",
"INFO\n\nSends information about the server, developers, etc.": "INFO\n\nSends information about the server, developers, etc.",
"INVITE <nickname> <channel>\n\nInvites the given user to the given channel, so long as you have the\nappropriate channel privs.": "INVITE <nickname> <channel>\n\nInvites the given user to the given channel, so long as you have the\nappropriate channel privs.",
"ISON <nickname>{ <nickname>}\n\nReturns whether the given nicks exist on the network.": "ISON <nickname>{ <nickname>}\n\nReturns whether the given nicks exist on the network.",
"JOIN <channel>{,<channel>} [<key>{,<key>}]\n\nJoins the given channels with the matching keys.": "JOIN <channel>{,<channel>} [<key>{,<key>}]\n\nJoins the given channels with the matching keys.",
"KICK <channel> <user> [reason]\n\nRemoves the user from the given channel, so long as you have the appropriate\nchannel privs.": "KICK <channel> <user> [reason]\n\nRemoves the user from the given channel, so long as you have the appropriate\nchannel privs.",
"KILL <nickname> [reason]\n\nRemoves the given user from the network, showing them the reason if it is\nsupplied.": "KILL <nickname> [reason]\n\nRemoves the given user from the network, showing them the reason if it is\nsupplied.",
"KLINE [ANDKILL] [MYSELF] [duration] <mask> [ON <server>] [reason [| oper reason]]\nKLINE LIST\n\nBans a mask from connecting to the server. If the duration is given then only for that\nlong. The reason is shown to the user themselves, but everyone else will see a standard\nmessage. The oper reason is shown to operators getting info about the KLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the KLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to KLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<mask> is specified in typical IRC format. For example:\n\tdan\n\tdan!5*@127.*\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"KLINE LIST\" is sent, the server sends back a list of our current KLINEs.": "KLINE [ANDKILL] [MYSELF] [duration] <mask> [ON <server>] [reason [| oper reason]]\nKLINE LIST\n\nBans a mask from connecting to the server. If the duration is given then only for that\nlong. The reason is shown to the user themselves, but everyone else will see a standard\nmessage. The oper reason is shown to operators getting info about the KLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the KLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to KLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<mask> is specified in typical IRC format. For example:\n\tdan\n\tdan!5*@127.*\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"KLINE LIST\" is sent, the server sends back a list of our current KLINEs.",
"LANGUAGE <code>{ <code>}\n\nSets your preferred languages to the given ones.": "LANGUAGE <code>{ <code>}\n\nSets your preferred languages to the given ones.",
"LIST [<channel>{,<channel>}] [<elistcond>{,<elistcond>}]\n\nShows information on the given channels (or if none are given, then on all\nchannels). <elistcond>s modify how the channels are selected.": "LIST [<channel>{,<channel>}] [<elistcond>{,<elistcond>}]\n\nShows information on the given channels (or if none are given, then on all\nchannels). <elistcond>s modify how the channels are selected.",
"LUSERS [<mask> [<server>]]\n\nShows statistics about the size of the network. If <mask> is given, only\nreturns stats for servers matching the given mask. If <server> is given, the\ncommand is processed by that server.": "LUSERS [<mask> [<server>]]\n\nShows statistics about the size of the network. If <mask> is given, only\nreturns stats for servers matching the given mask. If <server> is given, the\ncommand is processed by that server.",
"MODE <target> [<modestring> [<mode arguments>...]]\n\nSets and removes modes from the given target. For more specific information on\nmode characters, see the help for \"modes\".": "MODE <target> [<modestring> [<mode arguments>...]]\n\nSets and removes modes from the given target. For more specific information on\nmode characters, see the help for \"modes\".",
"MONITOR <subcmd>\n\nAllows the monitoring of nicknames, for alerts when they are online and\noffline. The subcommands are:\n\n MONITOR + target{,target}\nAdds the given names to your list of monitored nicknames.\n\n MONITOR - target{,target}\nRemoves the given names from your list of monitored nicknames.\n\n MONITOR C\nClears your list of monitored nicknames.\n\n MONITOR L\nLists all the nicknames you are currently monitoring.\n\n MONITOR S\nLists whether each nick in your MONITOR list is online or offline.": "MONITOR <subcmd>\n\nAllows the monitoring of nicknames, for alerts when they are online and\noffline. The subcommands are:\n\n MONITOR + target{,target}\nAdds the given names to your list of monitored nicknames.\n\n MONITOR - target{,target}\nRemoves the given names from your list of monitored nicknames.\n\n MONITOR C\nClears your list of monitored nicknames.\n\n MONITOR L\nLists all the nicknames you are currently monitoring.\n\n MONITOR S\nLists whether each nick in your MONITOR list is online or offline.",
"MOTD [server]\n\nReturns the message of the day for this, or the given, server.": "MOTD [server]\n\nReturns the message of the day for this, or the given, server.",
"NAMES [<channel>{,<channel>}]\n\nViews the clients joined to a channel and their channel membership prefixes. To\nview the channel membership prefixes supported by this server, see the help for\n\"PREFIX\".": "NAMES [<channel>{,<channel>}]\n\nViews the clients joined to a channel and their channel membership prefixes. To\nview the channel membership prefixes supported by this server, see the help for\n\"PREFIX\".",
"NICK <newnick>\n\nSets your nickname to the new given one.": "NICK <newnick>\n\nSets your nickname to the new given one.",
"NICKSERV <subcommand> [params]\n\nNickServ controls accounts and user registrations.": "NICKSERV <subcommand> [params]\n\nNickServ controls accounts and user registrations.",
"NOTICE <target>{,<target>} <text to be sent>\n\nSends the text to the given targets as a NOTICE.": "NOTICE <target>{,<target>} <text to be sent>\n\nSends the text to the given targets as a NOTICE.",
"NPC <target> <sourcenick> <text to be sent>\n\t\t\nThe NPC command is used to send a message to the target as the source.\n\nRequires the roleplay mode (+E) to be set on the target.": "NPC <target> <sourcenick> <text to be sent>\n\t\t\nThe NPC command is used to send a message to the target as the source.\n\nRequires the roleplay mode (+E) to be set on the target.",
"NPCA <target> <sourcenick> <text to be sent>\n\t\t\nThe NPC command is used to send an action to the target as the source.\n\nRequires the roleplay mode (+E) to be set on the target.": "NPCA <target> <sourcenick> <text to be sent>\n\t\t\nThe NPC command is used to send an action to the target as the source.\n\nRequires the roleplay mode (+E) to be set on the target.",
"NS <subcommand> [params]\n\nNickServ controls accounts and user registrations.": "NS <subcommand> [params]\n\nNickServ controls accounts and user registrations.",
"OPER <name> <password>\n\nIf the correct details are given, gives you IRCop privs.": "OPER <name> <password>\n\nIf the correct details are given, gives you IRCop privs.",
"PART <channel>{,<channel>} [reason]\n\nLeaves the given channels and shows people the given reason.": "PART <channel>{,<channel>} [reason]\n\nLeaves the given channels and shows people the given reason.",
"PASS <password>\n\nWhen the server requires a connection password to join, used to send us the\npassword.": "PASS <password>\n\nWhen the server requires a connection password to join, used to send us the\npassword.",
"PING <args>...\n\nRequests a PONG. Used to check link connectivity.": "PING <args>...\n\nRequests a PONG. Used to check link connectivity.",
"PONG <args>...\n\nReplies to a PING. Used to check link connectivity.": "PONG <args>...\n\nReplies to a PING. Used to check link connectivity.",
"PRIVMSG <target>{,<target>} <text to be sent>\n\nSends the text to the given targets as a PRIVMSG.": "PRIVMSG <target>{,<target>} <text to be sent>\n\nSends the text to the given targets as a PRIVMSG.",
"PROXY TCP4/6 <sourceip> <destip> <sourceport> <destport>\n\nUsed by haproxy's PROXY v1 protocol, to allow for alternate TLS support:\nhttp://www.haproxy.org/download/1.8/doc/proxy-protocol.txt": "PROXY TCP4/6 <sourceip> <destip> <sourceport> <destport>\n\nUsed by haproxy's PROXY v1 protocol, to allow for alternate TLS support:\nhttp://www.haproxy.org/download/1.8/doc/proxy-protocol.txt",
"QUIT [reason]\n\nIndicates that you're leaving the server, and shows everyone the given reason.": "QUIT [reason]\n\nIndicates that you're leaving the server, and shows everyone the given reason.",
"REHASH\n\nReloads the config file and updates TLS certificates on listeners": "REHASH\n\nReloads the config file and updates TLS certificates on listeners",
"RENAME <channel> <newname> [<reason>]\n\nRenames the given channel with the given reason, if possible.\n\nFor example:\n\tRENAME #ircv2 #ircv3 :Protocol upgrades!": "RENAME <channel> <newname> [<reason>]\n\nRenames the given channel with the given reason, if possible.\n\nFor example:\n\tRENAME #ircv2 #ircv3 :Protocol upgrades!",
"RESUME <oldnick> [timestamp]\n\nSent before registration has completed, this indicates that the client wants to\nresume their old connection <oldnick>.": "RESUME <oldnick> [timestamp]\n\nSent before registration has completed, this indicates that the client wants to\nresume their old connection <oldnick>.",
"RPL_ISUPPORT CASEMAPPING\n\nOragono supports an experimental unicode casemapping designed for extended\nUnicode support. This casemapping is based off RFC 7613 and the draft rfc7613\ncasemapping spec here: https://oragono.io/specs.html": "RPL_ISUPPORT CASEMAPPING\n\nOragono supports an experimental unicode casemapping designed for extended\nUnicode support. This casemapping is based off RFC 7613 and the draft rfc7613\ncasemapping spec here: https://oragono.io/specs.html",
"RPL_ISUPPORT PREFIX\n\nOragono supports the following channel membership prefixes:\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "RPL_ISUPPORT PREFIX\n\nOragono supports the following channel membership prefixes:\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.",
"SAMODE <target> [<modestring> [<mode arguments>...]]\n\nForcibly sets and removes modes from the given target -- only available to\nopers. For more specific information on mode characters, see the help for\n\"cmode\" and \"umode\".": "SAMODE <target> [<modestring> [<mode arguments>...]]\n\nForcibly sets and removes modes from the given target -- only available to\nopers. For more specific information on mode characters, see the help for\n\"cmode\" and \"umode\".",
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.",
"TIME [server]\n\nShows the time of the current, or the given, server.": "TIME [server]\n\nShows the time of the current, or the given, server.",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure",
"WHO <name> [o]\n\nReturns information for the given user.": "WHO <name> [o]\n\nReturns information for the given user.",
"WHOIS <client>{,<client>}\n\nReturns information for the given user(s).": "WHOIS <client>{,<client>}\n\nReturns information for the given user(s).",
"WHOWAS <nickname>\n\nReturns historical information on the last user with the given nickname.": "WHOWAS <nickname>\n\nReturns historical information on the last user with the given nickname."
}

View File

@ -0,0 +1,145 @@
{
"%d IRC Operators online": "%d IRC Operators online",
"%d channels formed": "%d channels formed",
"*** Could not find your username": "*** Could not find your username",
"*** Found your username": "*** Found your username",
"*** Got a malformed username, ignoring": "*** Got a malformed username, ignoring",
"*** Looking up your username": "*** Looking up your username",
"- %s Message of the day - ": "- %s Message of the day - ",
"Account already exists": "Account already exists",
"Account created": "Account created",
"Account name is not valid": "Account name is not valid",
"Account registration is disabled": "Account registration is disabled",
"Actual user@host, Actual IP": "Actual user@host, Actual IP",
"Added D-Line for %s": "Added D-Line for %s",
"Added K-Line for %s": "Added K-Line for %s",
"Added temporary (%s) D-Line for %s": "Added temporary (%s) D-Line for %s",
"Added temporary (%s) K-Line for %s": "Added temporary (%s) K-Line for %s",
"Authentication successful": "Authentication successful",
"Ban - %s - added by %s - %s": "Ban - %s - added by %s - %s",
"Callback namespace is not supported": "Callback namespace is not supported",
"Can't change modes for other users": "Can't change modes for other users",
"Can't view modes for other users": "Can't view modes for other users",
"Cannot join channel (+%s)": "Cannot join channel (+%s)",
"Cannot resume connection, connection registration has already been completed": "Cannot resume connection, connection registration has already been completed",
"Cannot resume connection, old and new clients must be logged into the same account": "Cannot resume connection, old and new clients must be logged into the same account",
"Cannot resume connection, old and new clients must have TLS": "Cannot resume connection, old and new clients must have TLS",
"Cannot resume connection, old client not found": "Cannot resume connection, old client not found",
"Cannot resume connection, old nickname contains spaces": "Cannot resume connection, old nickname contains spaces",
"Cannot send to channel": "Cannot send to channel",
"Channel %s successfully registered": "Channel %s successfully registered",
"Channel doesn't have roleplaying mode available": "Channel doesn't have roleplaying mode available",
"Channel list is full": "Channel list is full",
"Channel name is not valid": "Channel name is not valid",
"Channel registration is not enabled": "Channel registration is not enabled",
"Channel renamed: %s": "Channel renamed: %s",
"Client reconnected": "Client reconnected",
"Contributors and Former Developers:": "Contributors and Former Developers:",
"Core Developers:": "Core Developers:",
"Could not apply mode changes: +%s": "Could not apply mode changes: +%s",
"Could not parse IP address or CIDR network": "Could not parse IP address or CIDR network",
"Could not register": "Could not register",
"Could not remove ban [%s]": "Could not remove ban [%s]",
"Could not set or change nickname: %s": "Could not set or change nickname: %s",
"Could not successfully save new D-LINE: %s": "Could not successfully save new D-LINE: %s",
"Could not successfully save new K-LINE: %s": "Could not successfully save new K-LINE: %s",
"Credential type is not supported": "Credential type is not supported",
"End of /HELPOP": "End of /HELPOP",
"End of /WHOIS list": "End of /WHOIS list",
"End of LIST": "End of LIST",
"End of MOTD command": "End of MOTD command",
"End of NAMES list": "End of NAMES list",
"End of WHO list": "End of WHO list",
"End of WHOWAS": "End of WHOWAS",
"End of list": "End of list",
"Erroneous nickname": "Erroneous nickname",
"Fake source must be a valid nickname": "Fake source must be a valid nickname",
"First param must be a mask or channel": "First param must be a mask or channel",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.",
"Help not found": "Help not found",
"I have %d clients and %d servers": "I have %d clients and %d servers",
"Insufficient oper privs": "Insufficient oper privs",
"Invalid CAP subcommand": "Invalid CAP subcommand",
"JOIN 0 is not allowed": "JOIN 0 is not allowed",
"Language preferences have been set": "Language preferences have been set",
"Languages are not supported by this server": "Languages are not supported by this server",
"MOTD File is missing": "MOTD File is missing",
"Malformed username": "Malformed username",
"Mask isn't valid": "Mask isn't valid",
"NickServ is not yet implemented, sorry! To register an account, check /HELPOP REG": "NickServ is not yet implemented, sorry! To register an account, check /HELPOP REG",
"Nickname is already in use": "Nickname is already in use",
"No DLINEs have been set!": "No DLINEs have been set!",
"No command given": "No command given",
"No masks given": "No masks given",
"No nickname given": "No nickname given",
"No such channel": "No such channel",
"No such nick": "No such nick",
"No such server": "No such server",
"No topic is set": "No topic is set",
"Not enough parameters": "Not enough parameters",
"Only channel founders can change registered channels": "Only channel founders can change registered channels",
"Oragono is released under the MIT license.": "Oragono is released under the MIT license.",
"PROXY command is not usable from your address": "PROXY command is not usable from your address",
"Password incorrect": "Password incorrect",
"Permission Denied": "Permission Denied",
"Permission Denied - You're not an IRC operator": "Permission Denied - You're not an IRC operator",
"Proxied IP address is not valid: [%s]": "Proxied IP address is not valid: [%s]",
"Received malformed line": "Received malformed line",
"Rehashing": "Rehashing",
"Remote servers not yet supported": "Remote servers not yet supported",
"Removed D-Line for %s": "Removed D-Line for %s",
"Removed K-Line for %s": "Removed K-Line for %s",
"SASL authentication aborted": "SASL authentication aborted",
"SASL authentication failed": "SASL authentication failed",
"SASL authentication failed, you are not connecting with a certificate": "SASL authentication failed, you are not connecting with a certificate",
"SASL authentication failed: Bad account name": "SASL authentication failed: Bad account name",
"SASL authentication failed: Invalid auth blob": "SASL authentication failed: Invalid auth blob",
"SASL authentication failed: Invalid b64 encoding": "SASL authentication failed: Invalid b64 encoding",
"SASL authentication failed: Passphrase too long": "SASL authentication failed: Passphrase too long",
"SASL authentication failed: authcid and authzid should be the same": "SASL authentication failed: authcid and authzid should be the same",
"SASL authentication successful": "SASL authentication successful",
"SASL message too long": "SASL message too long",
"Server notice masks": "Server notice masks",
"Sorry, I don't know that command": "Sorry, I don't know that command",
"Syntax: REGISTER <channel>": "Syntax: REGISTER <channel>",
"Thanks to Jeremy Latt for founding Ergonomadic, the project this is based on <3": "Thanks to Jeremy Latt for founding Ergonomadic, the project this is based on <3",
"There are %d users and %d invisible on %d server(s)": "There are %d users and %d invisible on %d server(s)",
"There was no such nickname": "There was no such nickname",
"They aren't on that channel": "They aren't on that channel",
"This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>": "This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>",
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.",
"This server was created %s": "This server was created %s",
"Timestamp is not in 2006-01-02T15:04:05.999Z format, ignoring it": "Timestamp is not in 2006-01-02T15:04:05.999Z format, ignoring it",
"Unknown command": "Unknown command",
"Unknown subcommand": "Unknown subcommand",
"User doesn't have roleplaying mode enabled": "User doesn't have roleplaying mode enabled",
"VERIFY is not yet implemented": "VERIFY is not yet implemented",
"WEBIRC command is not usable from your address or incorrect password given": "WEBIRC command is not usable from your address or incorrect password given",
"Welcome to the Internet Relay Network %s": "Welcome to the Internet Relay Network %s",
"You are banned from this server (%s)": "You are banned from this server (%s)",
"You are no longer marked as being away": "You are no longer marked as being away",
"You are not using a TLS certificate": "You are not using a TLS certificate",
"You are now an IRC operator": "You are now an IRC operator",
"You are now logged in as %s": "You are now logged in as %s",
"You have been banned from this server (%s)": "You have been banned from this server (%s)",
"You have been marked as being away": "You have been marked as being away",
"You may not reregister": "You may not reregister",
"You must be an oper on the channel to register it": "You must be an oper on the channel to register it",
"You must be logged in to register a channel": "You must be logged in to register a channel",
"You need to register before you can use that command": "You need to register before you can use that command",
"You need to run a command": "You need to run a command",
"You specified too many languages": "You specified too many languages",
"You're already logged into an account": "You're already logged into an account",
"You're already opered-up!": "You're already opered-up!",
"You're not a channel operator": "You're not a channel operator",
"You're not on that channel": "You're not on that channel",
"Your host is %s, running version %s": "Your host is %s, running version %s",
"can speak these languages": "can speak these languages",
"has client certificate fingerprint %s": "has client certificate fingerprint %s",
"is a $bBot$b on %s": "is a $bBot$b on %s",
"is an unknown mode character to me": "is an unknown mode character to me",
"is logged in as": "is logged in as",
"is using a secure connection": "is using a secure connection",
"seconds idle, signon time": "seconds idle, signon time"
}

View File

@ -1,5 +1,11 @@
# language file for our example language
# if you're creating a new language file, base it off this one (this one contains all the strings you can replace)
# language info file for our example language
#
# languages are made up of a few different files:
# <locale>.lang.yaml - general info about the translation
# <locale>-irc.lang.json - IRC strings to be translated
# <locale>-help.lang.json - help entries to be translated
#
# we split up translations in this way so that they can be displayed more nicely on CrowdIn
# name - this is the 'nice' or 'full' name of the language
name: "Example"
@ -12,146 +18,3 @@ maintainers: "Daniel Oaks <daniel@danieloaks.net>"
# incomplete - whether to mark this language as incomplete
incomplete: true
# translations - this holds the actual replacements
# make sure this is the last part of the file, and that the below string, "translations:",
# stays as-is. the language-update processor uses the next line to designate which part of
# the file to ignore and which part to actually process.
translations:
"%d IRC Operators online": ""
"%d channels formed": ""
"*** Could not find your username": ""
"*** Found your username": ""
"*** Got a malformed username, ignoring": ""
"*** Looking up your username": ""
"- %s Message of the day - ": ""
"Account already exists": ""
"Account created": ""
"Account name is not valid": ""
"Account registration is disabled": ""
"Actual user@host, Actual IP": ""
"Added K-Line for %s": ""
"Added temporary (%s) K-Line for %s": ""
"Authentication successful": ""
"Ban - %s - added by %s - %s": ""
"Callback namespace is not supported": ""
"Can't change modes for other users": ""
"Can't view modes for other users": ""
"Cannot join channel (+%s)": ""
"Cannot resume connection, connection registration has already been completed": ""
"Cannot resume connection, old and new clients must be logged into the same account": ""
"Cannot resume connection, old and new clients must have TLS": ""
"Cannot resume connection, old client not found": ""
"Cannot resume connection, old nickname contains spaces": ""
"Cannot send to channel": ""
"Channel %s successfully registered": ""
"Channel doesn't have roleplaying mode available": ""
"Channel list is full": ""
"Channel name is not valid": ""
"Channel registration is not enabled": ""
"Channel renamed: %s": ""
"Client reconnected": ""
"Could not apply mode changes: +%s": ""
"Could not parse IP address or CIDR network": ""
"Could not register": ""
"Could not remove ban [%s]": ""
"Could not set or change nickname: %s": ""
"Could not successfully save new D-LINE: %s": ""
"Could not successfully save new K-LINE: %s": ""
"Credential type is not supported": ""
"End of /HELPOP": ""
"End of /WHOIS list": ""
"End of LIST": ""
"End of MOTD command": ""
"End of NAMES list": ""
"End of WHO list": ""
"End of WHOWAS": ""
"End of list": ""
"Erroneous nickname": ""
"Fake source must be a valid nickname": ""
"First param must be a mask or channel": ""
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": ""
"Help not found": ""
"I have %d clients and %d servers": ""
"Insufficient oper privs": ""
"Invalid CAP subcommand": ""
"JOIN 0 is not allowed": ""
"Language preferences have been set": ""
"Languages are not supported by this server": ""
"MOTD File is missing": ""
"Malformed username": ""
"Mask isn't valid": ""
"NickServ is not yet implemented, sorry! To register an account, check /HELPOP REG": ""
"Nickname is already in use": ""
"No DLINEs have been set!": ""
"No command given": ""
"No masks given": ""
"No nickname given": ""
"No such channel": ""
"No such nick": ""
"No such server": ""
"No topic is set": ""
"Not enough parameters": ""
"Only channel founders can change registered channels": ""
"PROXY command is not usable from your address": ""
"Password incorrect": ""
"Permission Denied": ""
"Permission Denied - You're not an IRC operator": ""
"Proxied IP address is not valid: [%s]": ""
"Received malformed line": ""
"Rehashing": ""
"Remote servers not yet supported": ""
"Removed D-Line for %s": ""
"Removed K-Line for %s": ""
"SASL authentication aborted": ""
"SASL authentication failed": ""
"SASL authentication failed, you are not connecting with a certificate": ""
"SASL authentication failed: Bad account name": ""
"SASL authentication failed: Invalid auth blob": ""
"SASL authentication failed: Invalid b64 encoding": ""
"SASL authentication failed: Passphrase too long": ""
"SASL authentication failed: authcid and authzid should be the same": ""
"SASL authentication successful": ""
"SASL message too long": ""
"Server notice masks": ""
"Sorry, I don't know that command": ""
"Syntax: REGISTER <channel>": ""
"There are %d users and %d invisible on %d server(s)": ""
"There was no such nickname": ""
"They aren't on that channel": ""
"This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>": ""
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": ""
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": ""
"This server was created %s": ""
"Timestamp is not in 2006-01-02T15:04:05.999Z format, ignoring it": ""
"Unknown command": ""
"Unknown subcommand": ""
"User doesn't have roleplaying mode enabled": ""
"VERIFY is not yet implemented": ""
"WEBIRC command is not usable from your address or incorrect password given": ""
"Welcome to the Internet Relay Network %s": ""
"You are banned from this server (%s)": ""
"You are no longer marked as being away": ""
"You are not using a TLS certificate": ""
"You are now an IRC operator": ""
"You are now logged in as %s": ""
"You have been banned from this server (%s)": ""
"You have been marked as being away": ""
"You may not reregister": ""
"You must be an oper on the channel to register it": ""
"You must be logged in to register a channel": ""
"You need to register before you can use that command": ""
"You need to run a command": ""
"You specified too many languages": ""
"You're already logged into an account": ""
"You're already opered-up!": ""
"You're not a channel operator": ""
"You're not on that channel": ""
"Your host is %s, running version %s": ""
"can speak these languages": ""
"has client certificate fingerprint %s": ""
"is a $bBot$b on %s": ""
"is an unknown mode character to me": ""
"is logged in as": ""
"is using a secure connection": ""
"seconds idle, signon time": ""

View File

@ -26,6 +26,7 @@ Options:
<languages-dir> Languages directory."""
import os
import re
import json
from docopt import docopt
import yaml
@ -34,7 +35,8 @@ if __name__ == '__main__':
arguments = docopt(__doc__, version="0.1.0")
if arguments['run']:
lang_strings = []
# general IRC strings
irc_strings = []
for subdir, dirs, files in os.walk(arguments['<irc-dir>']):
for fname in files:
@ -44,14 +46,32 @@ if __name__ == '__main__':
matches = re.findall(r'\.t\("((?:[^"]|\\")+)"\)', content)
for match in matches:
if match not in lang_strings:
lang_strings.append(match)
if match not in irc_strings:
irc_strings.append(match)
matches = re.findall(r'\.t\(\`([^\`]+)\`\)', content)
for match in matches:
match = match.replace("\n", "\\n").replace("\"", "\\\"")
if match not in lang_strings:
lang_strings.append(match)
if match not in irc_strings:
irc_strings.append(match)
for match in sorted(lang_strings):
print(' "' + match + '": ""')
print("irc strings:")
print(json.dumps({k:k for k in irc_strings}, sort_keys=True, indent=2, separators=(',', ': ')))
# help entries
help_strings = []
for subdir, dirs, files in os.walk(arguments['<irc-dir>']):
for fname in files:
filepath = subdir + os.sep + fname
if fname == 'help.go':
content = open(filepath, 'r').read()
matches = re.findall(r'\`([^\`]+)\`', content)
for match in matches:
if '\n' in match and match not in help_strings:
help_strings.append(match)
print()
print("help strings:")
print(json.dumps({k:k for k in help_strings}, sort_keys=True, indent=2, separators=(',', ': ')))