Merge remote-tracking branch 'origin/master' into listener_refactor.4

This commit is contained in:
Shivaram Lingamneni 2019-06-28 09:05:07 -04:00
commit cfe991a335
16 changed files with 96 additions and 91 deletions

View File

@ -47,5 +47,6 @@ archive:
- languages/*.yaml - languages/*.yaml
- languages/*.json - languages/*.json
- languages/*.md - languages/*.md
wrap_in_directory: true
checksum: checksum:
name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt" name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt"

View File

@ -1,8 +1,24 @@
# Changelog # Changelog
All notable changes to Oragono will be documented in this file. All notable changes to Oragono will be documented in this file.
## [1.1.0-rc1] - 2019-06-11 ## Unreleased
We're pleased to be publishing the release candidate for 1.1.0 (the official release should follow in a week or two, with more complete credits). This version has a number of exciting improvements, including: New release of Oragono!
### Config Changes
### Security
### Added
### Changed
### Removed
### Fixed
## [1.1.0] - 2019-06-27
We're pleased to announce Oragono version 1.1.0. This version has a number of exciting improvements, including:
* Simplified commands for registering new accounts with NickServ. * Simplified commands for registering new accounts with NickServ.
* Support for IP cloaking. * Support for IP cloaking.
@ -10,22 +26,29 @@ We're pleased to be publishing the release candidate for 1.1.0 (the official rel
* Support for the newly ratified [message tags](https://ircv3.net/specs/extensions/message-tags.html) and [message ID](https://ircv3.net/specs/extensions/message-ids.html) IRCv3 specifications; client developers are invited to use Oragono as a reference when implementing these specifications. * Support for the newly ratified [message tags](https://ircv3.net/specs/extensions/message-tags.html) and [message ID](https://ircv3.net/specs/extensions/message-ids.html) IRCv3 specifications; client developers are invited to use Oragono as a reference when implementing these specifications.
* Support for running Oragono as a Tor hidden service. * Support for running Oragono as a Tor hidden service.
This release includes a database change. If you have `datastore.autoupgrade` set to `true` in your configuration, it will be automatically applied when you restart Oragono; otherwise, you can update the database manually by running `oragono upgradedb`. Many thanks to [@Ascrod](https://github.com/Ascrod), [@amyspark](https://github.com/amyspark), [@bogdomania](https://github.com/bogdomania), [@csmith](https://github.com/csmith), [@jesopo](https://github.com/jesopo), [@jwheare](https://github.com/jwheare), lover, and [@transitracer](https://github.com/oragono/oragono/issues/456) for reporting issues and contributing patches, and also to [@bogdomania](https://github.com/bogdomania), Elvedin Hušić, Nuve, and [@streaps](https://github.com/streaps) for contributing translations.
### Upgrade notes
This release includes a database change. If you have `datastore.autoupgrade` set to `true` in your configuration, it will be automatically applied when you restart Oragono. Otherwise, you can update the database manually by running `oragono upgradedb` (see the manual for complete instructions).
No changes to your configuration file should be required for this upgrade. However, updating the file is necessary to enable some new functionality, as described below.
### Config changes ### Config changes
* `tor-listeners` section added for configuring listeners for use with Tor. * `tor-listeners` section added for configuring listeners for use with Tor.
* `compatibility` section added for toggling compatibility behaviors for legacy clients. * `compatibility` section added for toggling compatibility behaviors for legacy clients.
* `ip-cloaking` section added for configuring cloaking. * `ip-cloaking` section added for configuring cloaking.
* `bouncer` section added for configuring bouncer-like features (in particular, whether multiple clients can use the same nickname). * `bouncer` section added for configuring bouncer-like features (in particular, whether multiple clients can use the same nickname).
* `check-ident` now defaults to `false`. * `check-ident` now has recommended value `false`.
* `nick-reservation.method` now defaults to `"strict"`. * `nick-reservation.method` now has recommended value "strict"`.
* `fakelag.enabled` now defaults to `true` * `fakelag.enabled` now has recommended value `true`.
* `limits.linelen.tags` removed due to ratification of the [message-tags spec](https://ircv3.net/specs/extensions/message-tags.html), which fixes the maximum tags length at 8191 bytes. * `limits.linelen.tags` removed due to ratification of the [message-tags spec](https://ircv3.net/specs/extensions/message-tags.html), which fixes the maximum tags length at 8191 bytes.
* `limits.registration-messages` added to restrict how many messages a user can send to the server during connection registration (while connecting to the server). * `limits.registration-messages` added to restrict how many messages a user can send to the server during connection registration (while connecting to the server).
* `channels.operator-only-creation` added to optionally restrict creation of new channels to ircops (#537). * `channels.operator-only-creation` added to optionally restrict creation of new channels to ircops (#537).
### Security ### Security
* Users can no longer impersonate network services like ChanServ by using confusing nicks like "ChɑnServ" (#519, thanks [@csmith](https://github.com/csmith)!). * Users can no longer impersonate network services like ChanServ by using confusing nicks like "ChɑnServ" (#519, thanks [@csmith](https://github.com/csmith)!).
* Closed several loopholes in confusable nick detection (#562, #564, #570, thanks lover!)
* Secret channels (mode `+s`) now act more secret (#380, thanks [@csmith](https://github.com/csmith)!). * Secret channels (mode `+s`) now act more secret (#380, thanks [@csmith](https://github.com/csmith)!).
* The `+R` (registered-only) mode now prevents unregistered users from joining the channel, not just from speaking (#463, thanks [@bogdomania](https://github.com/bogdomania)!). * The `+R` (registered-only) mode now prevents unregistered users from joining the channel, not just from speaking (#463, thanks [@bogdomania](https://github.com/bogdomania)!).
* Limited how many messages clients can send during connection registration to mitigate potential DoS attacks (#505). * Limited how many messages clients can send during connection registration to mitigate potential DoS attacks (#505).
@ -59,6 +82,8 @@ This release includes a database change. If you have `datastore.autoupgrade` set
* Support for the [draft/event-playback](https://github.com/DanielOaks/ircv3-specifications/blob/master+event-playback/extensions/batch/history.md) spec (#457). * Support for the [draft/event-playback](https://github.com/DanielOaks/ircv3-specifications/blob/master+event-playback/extensions/batch/history.md) spec (#457).
* The `TAGMSG` and `NICK` messages are now replayable in history (#457). * The `TAGMSG` and `NICK` messages are now replayable in history (#457).
* Added the draft IRCv3 [`SETNAME` command](https://ircv3.net/specs/extensions/setname) for changing your realname (#372). * Added the draft IRCv3 [`SETNAME` command](https://ircv3.net/specs/extensions/setname) for changing your realname (#372).
* Added new Bosnian (bs-BA) translation (thanks to Elvedin Hušić!).
* Added new German (de-DE) translation (thanks to streaps!).
### Changed ### Changed
* Registering an account with NickServ is now `/msg NickServ register <password>`, which registers the current nickname as an account, matching other services (#410). * Registering an account with NickServ is now `/msg NickServ register <password>`, which registers the current nickname as an account, matching other services (#410).
@ -79,6 +104,8 @@ This release includes a database change. If you have `datastore.autoupgrade` set
* `NICKSERV ENFORCE` is deprecated in favor of the new `NICKSERV SET ENFORCE` (the old syntax is still available as an alias). * `NICKSERV ENFORCE` is deprecated in favor of the new `NICKSERV SET ENFORCE` (the old syntax is still available as an alias).
* The `WHO` command is now treated like `PONG` in that it doesn't count as user activity, since client software often uses it automatically (#485). * The `WHO` command is now treated like `PONG` in that it doesn't count as user activity, since client software often uses it automatically (#485).
* The `NAMES` command now only returns results for the first given channel (#534). * The `NAMES` command now only returns results for the first given channel (#534).
* Updated French (fr-FR) translation (thanks to Nuve!).
* Updated Română (ro-RO) translation (thanks to [@bogdomania](https://github.com/bogdomania)!).
### Internal Notes ### Internal Notes
* Building Oragono is now easier (#409). * Building Oragono is now easier (#409).

4
Gopkg.lock generated
View File

@ -62,11 +62,11 @@
revision = "9520e82c474b0a04dd04f8a40959027271bab992" revision = "9520e82c474b0a04dd04f8a40959027271bab992"
[[projects]] [[projects]]
digest = "1:7caf3ea977a13cd8b9a2e1ecef1ccaa8e38f831b4f6ffcb8bd0aa909c48afb3a" digest = "1:e7de6e4830c9d4fe1463c09a2ee15ec3eb9455c2ea916044675c413e8a9c6608"
name = "github.com/oragono/confusables" name = "github.com/oragono/confusables"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "d5dd03409482fae2457f0742be22782890f720c2" revision = "fe1cf31a24b01cac37194669863df51713e08e54"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@ -50,7 +50,7 @@
name = "github.com/oragono/go-ident" name = "github.com/oragono/go-ident"
[[constraint]] [[constraint]]
revision = "d5dd03409482fae2457f0742be22782890f720c2" revision = "fe1cf31a24b01cac37194669863df51713e08e54"
name = "github.com/oragono/confusables" name = "github.com/oragono/confusables"
[[constraint]] [[constraint]]

View File

@ -7,7 +7,7 @@ all: install
install: deps install: deps
./install.sh ./install.sh
release: release: deps
goreleaser --skip-publish --rm-dist goreleaser --skip-publish --rm-dist
capdefs: capdefs:

View File

@ -22,6 +22,7 @@ _Copyright © Daniel Oaks <daniel@danieloaks.net>, Shivaram Lingamneni <slingamn
- Installing - Installing
- Windows - Windows
- macOS / Linux / Raspberry Pi - macOS / Linux / Raspberry Pi
- Upgrading
- Features - Features
- User Accounts - User Accounts
- Nickname reservation - Nickname reservation
@ -135,6 +136,8 @@ On a non-systemd system, oragono can be configured to log to a file and used [lo
As long as you are using official releases or release candidates of Oragono, any backwards-incompatible changes should be described in the changelog. As long as you are using official releases or release candidates of Oragono, any backwards-incompatible changes should be described in the changelog.
In general, the config file format should be fully backwards and forwards compatible. Unless otherwise noted, no config file changes should be necessary when upgrading Oragono. However, the "config changes" section of the changelog will typically describe new sections that can be added to your config to enable new functionality, as well as changes in the recommended values of certain fields.
The database is versioned; upgrades that involve incompatible changes to the database require updating the database. If you have `datastore.autoupgrade` enabled in your config, the database will be backed up and upgraded when you restart your server when required. Otherwise, you can apply upgrades manually: The database is versioned; upgrades that involve incompatible changes to the database require updating the database. If you have `datastore.autoupgrade` enabled in your config, the database will be backed up and upgraded when you restart your server when required. Otherwise, you can apply upgrades manually:
1. Stop your server 1. Stop your server

View File

@ -77,7 +77,7 @@ CAPDEFS = [
), ),
CapDef( CapDef(
identifier="LabeledResponse", identifier="LabeledResponse",
name="draft/labeled-response", name="draft/labeled-response-0.2",
url="https://ircv3.net/specs/extensions/labeled-response.html", url="https://ircv3.net/specs/extensions/labeled-response.html",
standard="draft IRCv3", standard="draft IRCv3",
), ),

View File

@ -53,7 +53,7 @@ const (
// https://ircv3.net/specs/extensions/invite-notify-3.2.html // https://ircv3.net/specs/extensions/invite-notify-3.2.html
InviteNotify Capability = iota InviteNotify Capability = iota
// LabeledResponse is the draft IRCv3 capability named "draft/labeled-response": // LabeledResponse is the draft IRCv3 capability named "draft/labeled-response-0.2":
// https://ircv3.net/specs/extensions/labeled-response.html // https://ircv3.net/specs/extensions/labeled-response.html
LabeledResponse Capability = iota LabeledResponse Capability = iota
@ -135,7 +135,7 @@ var (
"echo-message", "echo-message",
"extended-join", "extended-join",
"invite-notify", "invite-notify",
"draft/labeled-response", "draft/labeled-response-0.2",
"draft/languages", "draft/languages",
"oragono.io/maxline-2", "oragono.io/maxline-2",
"message-tags", "message-tags",

View File

@ -1160,12 +1160,7 @@ func (session *Session) sendFromClientInternal(blocking bool, serverTime time.Ti
msg.SetTag("msgid", msgid) msg.SetTag("msgid", msgid)
} }
// attach server-time // attach server-time
if session.capabilities.Has(caps.ServerTime) { session.setTimeTag(&msg, serverTime)
if serverTime.IsZero() {
serverTime = time.Now().UTC()
}
msg.SetTag("time", serverTime.Format(IRCv3TimestampFormat))
}
return session.SendRawMessage(msg, blocking) return session.SendRawMessage(msg, blocking)
} }
@ -1246,12 +1241,19 @@ func (client *Client) Send(tags map[string]string, prefix string, command string
func (session *Session) Send(tags map[string]string, prefix string, command string, params ...string) (err error) { func (session *Session) Send(tags map[string]string, prefix string, command string, params ...string) (err error) {
msg := ircmsg.MakeMessage(tags, prefix, command, params...) msg := ircmsg.MakeMessage(tags, prefix, command, params...)
if session.capabilities.Has(caps.ServerTime) && !msg.HasTag("time") { session.setTimeTag(&msg, time.Time{})
msg.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
}
return session.SendRawMessage(msg, false) return session.SendRawMessage(msg, false)
} }
func (session *Session) setTimeTag(msg *ircmsg.IrcMessage, serverTime time.Time) {
if session.capabilities.Has(caps.ServerTime) && !msg.HasTag("time") {
if serverTime.IsZero() {
serverTime = time.Now()
}
msg.SetTag("time", serverTime.UTC().Format(IRCv3TimestampFormat))
}
}
// Notice sends the client a notice from the server. // Notice sends the client a notice from the server.
func (client *Client) Notice(text string) { func (client *Client) Notice(text string) {
client.Send(nil, client.server.name, "NOTICE", client.Nick(), text) client.Send(nil, client.server.name, "NOTICE", client.Nick(), text)

View File

@ -9,7 +9,7 @@ import "fmt"
const ( const (
// SemVer is the semantic version of Oragono. // SemVer is the semantic version of Oragono.
SemVer = "1.1.0-rc1" SemVer = "1.2.0-unreleased"
) )
var ( var (

View File

@ -93,9 +93,7 @@ func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMa
msg.SetTag("msgid", msgid) msg.SetTag("msgid", msgid)
} }
// attach server-time // attach server-time
if rb.session.capabilities.Has(caps.ServerTime) && !msg.HasTag("time") { rb.session.setTimeTag(&msg, time)
msg.SetTag("time", time.UTC().Format(IRCv3TimestampFormat))
}
rb.AddMessage(msg) rb.AddMessage(msg)
} }
@ -212,24 +210,28 @@ func (rb *ResponseBuffer) flushInternal(final bool, blocking bool) error {
} }
useLabel := rb.session.capabilities.Has(caps.LabeledResponse) && rb.Label != "" useLabel := rb.session.capabilities.Has(caps.LabeledResponse) && rb.Label != ""
// use a batch if we have a label, and we either currently have 0 or 2+ messages, // use a batch if we have a label, and we either currently have 2+ messages,
// or we are doing a Flush() and we have to assume that there will be more messages // or we are doing a Flush() and we have to assume that there will be more messages
// in the future. // in the future.
useBatch := useLabel && (len(rb.messages) != 1 || !final) startBatch := useLabel && (1 < len(rb.messages) || !final)
// if label but no batch, add label to first message if startBatch {
if useLabel && !useBatch && len(rb.messages) == 1 && rb.batchID == "" {
rb.messages[0].SetTag(caps.LabelTagName, rb.Label)
} else if useBatch {
rb.sendBatchStart(blocking) rb.sendBatchStart(blocking)
} else if useLabel && len(rb.messages) == 0 && rb.batchID == "" && final {
// ACK message
message := ircmsg.MakeMessage(nil, rb.session.client.server.name, "ACK")
message.SetTag(caps.LabelTagName, rb.Label)
rb.session.setTimeTag(&message, time.Time{})
rb.session.SendRawMessage(message, blocking)
} else if useLabel && len(rb.messages) == 1 && rb.batchID == "" && final {
// single labeled message
rb.messages[0].SetTag(caps.LabelTagName, rb.Label)
} }
// send each message out // send each message out
for _, message := range rb.messages { for _, message := range rb.messages {
// attach server-time if needed // attach server-time if needed
if rb.session.capabilities.Has(caps.ServerTime) && !message.HasTag("time") { rb.session.setTimeTag(&message, time.Time{})
message.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
}
// attach batch ID, unless this message was part of a nested batch and is // attach batch ID, unless this message was part of a nested batch and is
// already tagged // already tagged

View File

@ -108,26 +108,6 @@ func CasefoldName(name string) (string, error) {
return lowered, err return lowered, err
} }
// "boring" names are exempt from skeletonization.
// this is because confusables.txt considers various pure ASCII alphanumeric
// strings confusable: 0 and O, 1 and l, m and rn. IMO this causes more problems
// than it solves.
func isBoring(name string) bool {
for i := 0; i < len(name); i += 1 {
chr := name[i]
if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') {
continue // alphanumerics
}
switch chr {
case '$', '%', '^', '&', '(', ')', '{', '}', '[', ']', '<', '>', '=':
continue // benign printable ascii characters
default:
return false // potentially confusable ascii like | ' `, non-ascii
}
}
return true
}
// returns true if the given name is a valid ident, using a mix of Insp and // returns true if the given name is a valid ident, using a mix of Insp and
// Chary's ident restrictions. // Chary's ident restrictions.
func isIdent(name string) bool { func isIdent(name string) bool {
@ -168,9 +148,7 @@ func Skeleton(name string) (string, error) {
// same as PRECIS: // same as PRECIS:
name = width.Fold.String(name) name = width.Fold.String(name)
if !isBoring(name) { name = confusables.SkeletonTweaked(name)
name = confusables.Skeleton(name)
}
// internationalized lowercasing for skeletons; this is much more lenient than // internationalized lowercasing for skeletons; this is much more lenient than
// Casefold. In particular, skeletons are expected to mix scripts (which may // Casefold. In particular, skeletons are expected to mix scripts (which may

View File

@ -128,18 +128,6 @@ func TestCasefoldName(t *testing.T) {
} }
} }
func TestIsBoring(t *testing.T) {
assertBoring := func(str string, expected bool) {
if isBoring(str) != expected {
t.Errorf("expected [%s] to have boringness [%t], but got [%t]", str, expected, !expected)
}
}
assertBoring("warning", true)
assertBoring("phi|ip", false)
assertBoring("Νικηφόρος", false)
}
func TestIsIdent(t *testing.T) { func TestIsIdent(t *testing.T) {
assertIdent := func(str string, expected bool) { assertIdent := func(str string, expected bool) {
if isIdent(str) != expected { if isIdent(str) != expected {
@ -173,15 +161,15 @@ func TestSkeleton(t *testing.T) {
t.Errorf("but we still consider pipe confusable with l") t.Errorf("but we still consider pipe confusable with l")
} }
if skeleton("") != "smt" { if skeleton("") != skeleton("smt") {
t.Errorf("fullwidth characters should skeletonize to plain old ascii characters") t.Errorf("fullwidth characters should skeletonize to plain old ascii characters")
} }
if skeleton("") != "smt" { if skeleton("") != skeleton("smt") {
t.Errorf("after skeletonizing, we should casefold") t.Errorf("after skeletonizing, we should casefold")
} }
if skeleton("sm") != "smt" { if skeleton("sm") != skeleton("smt") {
t.Errorf("our friend lover successfully tricked the skeleton algorithm!") t.Errorf("our friend lover successfully tricked the skeleton algorithm!")
} }
@ -189,6 +177,10 @@ func TestSkeleton(t *testing.T) {
t.Errorf("we must protect against cyrillic homoglyph attacks") t.Errorf("we must protect against cyrillic homoglyph attacks")
} }
if skeleton("еmily") != skeleton("emily") {
t.Errorf("we must protect against cyrillic homoglyph attacks")
}
if skeleton("РОТАТО") != "potato" { if skeleton("РОТАТО") != "potato" {
t.Errorf("we must protect against cyrillic homoglyph attacks") t.Errorf("we must protect against cyrillic homoglyph attacks")
} }

View File

@ -1,14 +1,14 @@
{ {
"$bWarning: unregistering this account will remove its stored privileges.$b": "$bAttention : effacer ce compte lèvera ses privilèges.$b", "$bWarning: unregistering this account will remove its stored privileges.$b": "$bAttention : effacer ce compte lèvera ses privilèges.$b",
"$bWarning: unregistering this channel will remove all stored channel attributes.$b": "$bAttention : effacer ce canal le démunira de ses attributs.$b", "$bWarning: unregistering this channel will remove all stored channel attributes.$b": "$bAttention : effacer ce canal le démunira de ses attributs.$b",
"%[1]d. User %[2]s requests vhost: %[3]s": "", "%[1]d. User %[2]s requests vhost: %[3]s": "%[1]d. %[2]s demande lhôtel virtuel : %[3]s",
"%[1]s [account: %[2]s] joined the channel": "", "%[1]s [account: %[2]s] joined the channel": "%[1]s [%[2]s] a rejoint le canal",
"%[1]s changed nick to %[2]s": "%[1]s a changé de nom pour %[2]s", "%[1]s changed nick to %[2]s": "%[1]s a changé de nom pour %[2]s",
"%[1]s kicked %[2]s (%[3]s)": "%[1]s a éjecté %[2]s (%[3]s)", "%[1]s kicked %[2]s (%[3]s)": "%[1]s a éjecté %[2]s (%[3]s)",
"%[1]s left the channel (%[2]s)": "%[1]s a quitté le canal (%[2]s)", "%[1]s left the channel (%[2]s)": "%[1]s a quitté le canal (%[2]s)",
"%[1]s quit (%[2]s)": "", "%[1]s quit (%[2]s)": "%[1]s est parti·e (%[2]s)",
"%s joined the channel": "%s a rejoint le canal", "%s joined the channel": "%s a rejoint le canal",
"*** $bEnd of %s HELP$b ***": "", "*** $bEnd of %s HELP$b ***": "*** $bFin de %s AIDE$b ***",
"*** Could not find your username": "*** Impossible de trouver votre nom dutilisateurice", "*** Could not find your username": "*** Impossible de trouver votre nom dutilisateurice",
"*** Found your username": "*** Nom dutilisateurice trouvé", "*** Found your username": "*** Nom dutilisateurice trouvé",
"*** Got a malformed username, ignoring": "*** Reçu mauvais nom dutilisateurice", "*** Got a malformed username, ignoring": "*** Reçu mauvais nom dutilisateurice",
@ -42,7 +42,7 @@
"Bouncer functionality is currently disabled for your account, but you can opt in": "", "Bouncer functionality is currently disabled for your account, but you can opt in": "",
"Bouncer functionality is currently enabled for your account": "", "Bouncer functionality is currently enabled for your account": "",
"Bouncer functionality is currently enabled for your account, but you can opt out": "", "Bouncer functionality is currently enabled for your account, but you can opt out": "",
"CTCP messages are disabled over Tor": "", "CTCP messages are disabled over Tor": "Les messages CTCP sont désactivés via Tor",
"Can't change modes for other users": "Impossible de changer les modes dautres utilisateurices", "Can't change modes for other users": "Impossible de changer les modes dautres utilisateurices",
"Can't view modes for other users": "Impossible de voir les modes des autres utilisateurices", "Can't view modes for other users": "Impossible de voir les modes des autres utilisateurices",
"Cannot join channel (+%s)": "Impossible de joindre (+%s)", "Cannot join channel (+%s)": "Impossible de joindre (+%s)",
@ -57,7 +57,7 @@
"Channel %[1]s has %[2]d persistent modes set": "", "Channel %[1]s has %[2]d persistent modes set": "",
"Channel %s is now unregistered": "", "Channel %s is now unregistered": "",
"Channel %s successfully registered": "", "Channel %s successfully registered": "",
"Channel does not exist": "", "Channel does not exist": "Ce canal nexiste pas",
"Channel doesn't have roleplaying mode available": "", "Channel doesn't have roleplaying mode available": "",
"Channel is not registered": "", "Channel is not registered": "",
"Channel list is full": "La liste de canaux est pleine", "Channel list is full": "La liste de canaux est pleine",
@ -91,11 +91,11 @@
"End of list": "Fin de liste", "End of list": "Fin de liste",
"Erroneous nickname": "Nom inadéquat", "Erroneous nickname": "Nom inadéquat",
"Error loading account data": "", "Error loading account data": "",
"Error reserving nickname": "", "Error reserving nickname": "Erreur lors de la réservation du nom",
"Error while unregistering account": "Erreur au cours de la suppression du compte", "Error while unregistering account": "Erreur au cours de la suppression du compte",
"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": "",
"GHOSTed by %s": "", "GHOSTed by %s": "Déconnecté·e via Ghost par %s",
"Given current server settings, your nickname is enforced with: %s": "", "Given current server settings, your nickname is enforced with: %s": "",
"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": "Aide introuvable", "Help not found": "Aide introuvable",
@ -103,10 +103,10 @@
"I have %[1]d clients and %[2]d servers": "Jai %[1]d client(s), et %[2]d serveur(s)", "I have %[1]d clients and %[2]d servers": "Jai %[1]d client(s), et %[2]d serveur(s)",
"I'll be right back": "De retour dans un instant!", "I'll be right back": "De retour dans un instant!",
"IP address: %s": "Adresse IP : %s", "IP address: %s": "Adresse IP : %s",
"IRC Operators online": "", "IRC Operators online": "Opérateurices IRC en ligne",
"Input line too long": "", "Input line too long": "Entrée trop longue",
"Insufficient oper privs": "", "Insufficient oper privs": "Privilèges opérateurices insuffisants",
"Insufficient privileges": "", "Insufficient privileges": "Privilèges insuffisants",
"Internal error": "Erreur interne", "Internal error": "Erreur interne",
"Invalid CAP subcommand": "", "Invalid CAP subcommand": "",
"Invalid account name": "Nom de compte invalide", "Invalid account name": "Nom de compte invalide",
@ -115,11 +115,11 @@
"Invalid vhost": "Vhost invalide", "Invalid vhost": "Vhost invalide",
"It was rejected for reason: %s": "", "It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "", "JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "", "Language %s is not supported by this server": "Le langage %s nest pas proposé sur ce serveur",
"Language preferences have been set": "", "Language preferences have been set": "Vos préférences linguistiques ont été enregistrées",
"Last active: %s": "", "Last active: %s": "Dernière activité : %s",
"MOTD File is missing": "Message du jour manquant", "MOTD File is missing": "Message du jour manquant",
"Malformed username": "", "Malformed username": "Nom erroné",
"Mask isn't valid": "Masque invalide", "Mask isn't valid": "Masque invalide",
"Must register with current nickname instead of separate account name": "", "Must register with current nickname instead of separate account name": "",
"Network service, for more info /msg %s HELP": "", "Network service, for more info /msg %s HELP": "",

View File

@ -1,5 +1,5 @@
--- ---
name: "Français" name: "Français"
code: "fr-FR" code: "fr-FR"
contributors: "Joshua Kwan <joshk@triplehelix.org>" contributors: "Joshua Kwan <joshk@triplehelix.org>, Nuve"
incomplete: true incomplete: true

2
vendor

@ -1 +1 @@
Subproject commit 8ddbb531841add50f8b7aff8fe00bef311448aaa Subproject commit 6fb1b63d24a6ccc28a8aecbd47a7eb18a2ace2c9