It's not as simple as supporting `local` or not. There is a lot of variation on the syntax and how it's done between shells that have one form or other of local scope.

That's why it's very hard to come up with a standard that agrees with all. See http://austingroupbugs.net/bug_view_page.php?bug_id=767 for the POSIX effort on that front.

local scope was added first in ksh in the early 80s.

The syntax to declare a local variable in a function was with `typeset`:

 function f {
 typeset var=value
 set -o noglob # also local to the function
 ...
 }

(function support was added to the Bourne shell later, but with a different syntax (`f() command`) and `ksh` added support for that one as well later; the Bourne shell never had local scope (except of course via subshells))

The `local` builtin AFAIK was added first to the Almquist shell (used in BSDs, dash, busybox sh) in 1989, but works significantly differently from `ksh`'s `typeset`. `ash` derivatives don't support `typeset` as an alias to `local`, but you can always define one by hand.

bash and zsh added `typeset` aliased to `local` in 1989 and 1991 respectively.

ksh88 added `local` as an undocumented alias to `typeset` circa 1990 and pdksh and its derivatives in 1994. `posh` (based on `pdksh`) removed `typeset` (for strict compliance to the Debian Policy that requires `local`, but not `typeset`).

POSIX initially objected to specifying `typeset` on the ground that it was _dynamic_ scoping. So ksh93 (a rewrite of ksh in 1993 by David Korn) switched to _static_ scoping instead. Also in ksh93, as opposed to ksh88, local scoping is only done for functions declared with the `ksh` syntax (`function f {...}`), not the Bourne syntax (`f() {...}`) and the `local` alias was removed.

However the ksh93v- beta and final version from AT&T can be compiled with an experimental "bash" mode (actually enabled by default) that does dynamic scoping (in bother forms of functions, including with `local` and `typeset`) when `ksh93` is invoked as `bash`. `local` differs from `typeset` in that case in that it can only be invoked from within a function. [That `bash` mode will be disabled by default in ksh2020](https://github.com/att/ast/issues/238) though [the `local`/`declare` aliases to `typeset` will be retained even when the bash mode is not compiled in](https://github.com/att/ast/pull/1379) (though still with static scoping).

`yash` (written much later), has `typeset` (à la ksh88), but has only had `local` as an alias to it since version 2.48 (December 2018).

[@Schily](/users/120884/schily) (who sadly [passed away in 2021](//unix.meta.stackexchange.com/q/5861)) used to maintain a Bourne shell descendant which has been recently made mostly POSIX compliant, called [`bosh`](http://schillix.sourceforge.net/man/man1/bosh.1.html) that supports local scope since version 2016-07-06 (with `local`, similar to `ash`).

So the Bourne-like shells that have some form of local scope for variables today are:

- ksh, all implementations and their derivatives (ksh88, ksh93, pdksh and derivatives like posh, mksh, OpenBSD sh).
- ash and all its derivatives (NetBSD sh, FreeBSD sh, dash, busybox sh)
- bash
- zsh
- yash
- bosh

As far as the `sh` of different systems go, note that there are systems where the POSIX `sh` is in `/bin` (most), and others where it's not (like Solaris where it's in `/usr/xpg4/bin`). For the `sh` implementation on various systems we have:

- ksh88: most SysV-derived commercial Unices (AIX, HP/UX, Solaris¹...)
- bash: most GNU/Linux systems, Cygwin, macOS
- ash: by default on Debian and most derivatives (including Ubuntu, Linux/Mint) though can be changed by the admin to bash or mksh. NetBSD, FreeBSD and some of their derivatives (not macOS).
- busybox sh: many if not most embedded Linux systems
- pdksh or derivatives: OpenBSD, MirBSD, Android

Now, where they differ:

- `typeset` (ksh, pdksh, bash, zsh, yash) vs `local` (ksh88, pdksh, bash, zsh, ash, yash 2.48+).
- the list of supported options.
- static (ksh93, in `function f {...}` function), vs dynamic scoping (all other shells). For instance, whether `function f { typeset v=1; g; echo "$v"; }; function g { v=2; }; f` outputs `1` or `2`. See also how the `export` attribute affects scoping in `ksh93`.
- whether `local`/`typeset` just makes the variable _local_ (`ash`, `bosh`), or creates a new instance of the variable (other shells). For instance, whether `v=1; f() { local v; echo "${v:-empty}"; }; f` outputs `1` or `empty` (see also the `localvar_inherit` option in bash 5.0 and above).
- with those that create a new variable, whether the new one inherits the _attributes_ (like `export`) and/or type and which ones from the variable in the parent scope. For instance, whether `export V=1; f() { local V=2; printenv V; }; f` prints `1`, `2` or nothing.
- whether that new variable has an initial value (empty, 0, empty list, depending on type, `zsh`) or is initially unset.
- whether `unset V` on a variable in a local scope leaves the variable `unset`, or just _peels_ one level of scoping (`mksh`, `yash`, `bash` under some circumstances). For instance, whether `v=1; f() { local v=2; unset v; echo "$v"; }` outputs `1` or nothing (see also the `localvar_unset` option in bash 5.0 and above)
- like for `export`, whether it's a keyword or only a mere builtin or both and under what condition it's considered as a keyword.
- like for `export`, whether the arguments are parsed as normal command arguments or as assignments (and under what condition).
- whether you can declare _local_ a variable that was readonly in the parent scope.
- the interactions with `v=value myfunction` where `myfunction` itself declares `v` as local or not.

That's the ones I'm thinking of just now. Check the austin group bug above for more details.

As far as local scoping for shell *options* (as opposed to *variables*), shells supporting it are:

- `ksh88` (with both function definition syntax): done by default, I'm not aware of any way to disable it.
- `ash` (since 1989): with `local -`. It makes the `$-` parameter (which stores the list of options) local.
- `ksh93`: now only done for `function f {...}` functions.
- `zsh` (since 1995). With `setopt localoptions`. Also with `emulate -L` for the emulation mode (and its set of options) to be made local to the function.
- `bash` (since 2016) with `local -` like in `ash`, but only for the options managed by `set`, not the ones managed by `shopt`.

---
<sup>¹ the POSIX `sh` on Solaris is `/usr/xpg4/bin/sh` (though it has many conformance bugs including those options local to functions). `/bin/sh` up to Solaris 10 was the Bourne shell (so no local scope), and since Solaris 11 is ksh93</sup>