How can I represent a 2-dimensional array in Protocol Buffers?

You could try structpb. like this:

.proto

import "google/protobuf/struct.proto";
message Foo {
    repeated google.protobuf.Value array = 1;
}

.go

import (
    "google.golang.org/protobuf/types/known/structpb"
    "google.golang.org/protobuf/encoding/protojson"
)

...
foo := &Foo{}

items := []interface{}{1,2,3,4,5}
l, err := structpb.NewList(items)
foo.array = append(foo.array, structpb.NewListValue(l))

protojson.Format(foo)  // [[1,2,3,4,5]]

There is no direct support in the protocol for this. Your best bet is to have a repeated set of objects that have an array each - i.e.

message Foo {
    repeated int items = 1;
}
...
repeated Foo foos = 1;